#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <ctype.h>
#include <strings.h>
#include <time.h>
#include <getopt.h>

#include <libwr/shw_io.h>
#include "fpga_io.h"
#include <libwr/switch_hw.h>

#define SPLL_WB_BASE        0x10300
#define SPLL_HO_CR          0x54
#define SPLL_HO_RATE        0x58
#define SPLL_FUNC_SEL_MSK   0x00000F00
#define SPLL_LRN_EN_MSK     0x00000010
#define SPLL_HO_EN_MSK      0x00000001

#define HO_ON   1
#define HO_OFF  0
#define LRN_ON  1
#define LRN_OFF 0


void error(char *msg)
{
	perror(msg);
	exit(0);
}

void printHelp(char *name)
{
	printf("WRS Holdover tool\n\n");
	printf("This is a tool to allow the user to control the WRITE holdover parameters:\n");
	printf(" - Select holdover function\n");
	printf(" - Select rate of data acquisition\n");
	printf(" - Enable/disable learning\n");
	printf(" - Enable/disable holdover\n\n");
	printf("Usage: %s --func [0-15]     : selects holdover function to be used.\n",name);
	printf("       %s --rate [rate]     : determines that 1 out of [rate] datapoints will be \n",name);
	printf("                              included in the circular buffers\n");
	printf("       %s --learn [on/off]  : determines if the circular buffers are being updated.\n",name);
	printf("       %s --active [on/off] : determines if the holdover estimation is governing the clock output.\n",name);
	exit(0);
}

void sigintHandler(int sig_num)
{
	exit(0);
}

int set_function(int value)
{
	// Sanitize input arg:
	if(value > 15 || value < 0)
	{
		printf("Introduced func %d out of range. Function must be in the range [0-15].\n",value);
		printf("Exit without change.\n");
		return -1;
	}
	uint32_t tmp = _fpga_readl(SPLL_WB_BASE+SPLL_HO_CR);
	tmp = tmp | (~SPLL_FUNC_SEL_MSK);
	tmp = tmp | (value << 8);
	_fpga_writel(SPLL_WB_BASE+SPLL_HO_CR,tmp);
	return 0;
}

int set_rate(int value)
{
	_fpga_writel(SPLL_WB_BASE+SPLL_HO_RATE, value);
	return 0;
}

int set_learning(int value)
{
	uint32_t tmp = _fpga_readl(SPLL_WB_BASE+SPLL_HO_CR)
	if(value==1)
	{
		_fpga_writel(SPLL_WB_BASE+SPLL_HO_CR, tmp | SPLL_LRN_EN_MSK);
		return 0;
	}else if(value==0){
		_fpga_writel(SPLL_WB_BASE+SPLL_HO_CR, tmp & (~SPLL_LRN_EN_MSK));
		return 0;
	}else{
		return -1;
	}
}

int set_holdover(int value)
{
	uint32_t tmp = _fpga_readl(SPLL_WB_BASE+SPLL_HO_CR)
	if(value==1)
	{
		_fpga_writel(SPLL_WB_BASE+SPLL_HO_CR, tmp | SPLL_HO_EN_MSK);
		return 0;
	}else if(value==0){
		_fpga_writel(SPLL_WB_BASE+SPLL_HO_CR, tmp & (~SPLL_HO_EN_MSK));
		return 0;
	}else{
		return -1;
	}
}

int main(int argc, char *argv[])
{
	int c = 0;
    int option_index = 0;
    int value = 0;
	int ret = -1
    char s_status[4];

    static struct option long_options[] =
    {
        {"help",    no_argument,        0, 'h'},
        {"func",    required_argument,  0, 'f'},
        {"rate",    required_argument,  0, 'r'},
        {"learn",   required_argument,  0, 'l'},
        {"active",  required_argument,  0, 'a'},
        {0,0,0,0}
    };


	while ((c = getopt_long(argc, argv, "hf:r:l:a:",long_options, &option_index)) != -1)
    {
		switch (c)
		{
		case 'h':
			printHelp(argv[0]);
			break;
		case 'f':
			value = atoi(optarg);
            ret = set_function(value);
			break;
		case 'r':
			value = atoi(optarg);
			ret = set_rate(value);
            break;
		case 'l':
			strcpy(s_status, optarg);
            if(!strcasecmp(s_status,"on"))
            {
                ret = set_learning(LRN_ON)
                break;
            }
            else if(!strcasecmp(s_status,"off"))
            {
                ret = set_learning(LRN_OFF)
                break;
            }
			break;
		case 'a':
			strcpy(s_status, optarg);
            if(!strcasecmp(s_status,"on"))
            {
                ret = set_holdover(HO_ON);
                break;
            }
            else if(!strcasecmp(s_status,"off"))
            {
                ret = set_holdover(HO_OFF)
                break;
            }
			break;
		case '?':
			printHelp(argv[0]);
            break;
		default:
			abort();
		}
    }

    shw_fpga_mmap_init();

	return ret;
	
}
