#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include "../libsample/sample.h"
#include "amps.h"
#include "frame.h"
#include "main.h"

static struct sysinfo_reg_incr default_reg_incr = {
	450,
};

static struct sysinfo_loc_area default_loc_area = {
	0,
	0,
	0,
	0,
};

static struct sysinfo_new_acc default_new_acc = {
	0,
};

static struct sysinfo_overload default_overload = {
	{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
};

static struct sysinfo_acc_type default_acc_type = {
	1,
	0,
	0,
	0,
	0,
};

static struct sysinfo_acc_attempt default_acc_attempt = {
	10,
	10,
	10,
	10,
};

void init_sysinfo(amps_si *si, int cmac, int vmac, int dtx, int dcc, int sid1, int regh, int regr, int pureg, int pdreg, int locaid, int regincr, int bis)
{
	int i;

	memset(si, 0, sizeof(*si));

	/* how oftern to repeat the overhead train */
	if (!tacs)
		si->overhead_repeat = 17;
	else
		si->overhead_repeat = 14;

	/* all words */
	si->dcc = dcc;
	/* VC assignment */
	si->vmac = vmac;

	/* filler */
	si->filler.cmac = cmac;
	si->filler.sdcc1 = 0;
	si->filler.sdcc2 = 0;
	si->filler.wfom = (bis) ? 0 : 1; /* must be set to ignore B/I bit */

	/* Word 1 */
	si->word1.sid1 = sid1;
	si->word1.ep = 0; /* shall be 0 */
	si->word1.auth = 0;
	si->word1.pci = 0;

	/* Word 2 */
	si->word2.s = 1;
	si->word2.e = 1;
	si->word2.regh = regh;
	si->word2.regr = regr;
	si->word2.dtx = dtx; /* DTX seems not to work with my White Dynatac 8000 */
	si->word2.n_1 = 20;
	si->word2.rcf = (bis) ? 0 : 1; /* must be set to ignore B/I bit */
	si->word2.cpa = 1; /* must be set for combined CC+PC */
	si->word2.cmax_1 = 20;

	/* registration increment */
	si->reg_incr.regincr = regincr;

	/* location area */
	si->loc_area.pureg = pureg;
	si->loc_area.pdreg = pdreg;
	if (locaid >= 0) {
		si->loc_area.lreg = 1;
		si->loc_area.locaid = locaid;
	}

	/* new access channel set */
	si->new_acc.newacc = 0;

	/* overload control */
	for (i = 0; i < 16; i++)
		si->overload.olc[i] = 1;

	/* Access Tyoe */
	/* 'bis' must be 0, so the phone does not wait for busy bit.
	 * We cannot respond with B/I fast enough due to processing delay.
	 * So we don't set the B/I bit to busy on reception of message.
	 * The access type message (including this 'bis') must also be included.
	 */
	si->acc_type.bis = bis; /* must be clear to ignore B/I bit */
	si->acc_type.pci_home = 0; /* if set, bscap must also be set */
	si->acc_type.pci_roam = 0; /* if set, bscap must also be set */
	si->acc_type.bspc = 0;
	si->acc_type.bscap = 0;

	/* access attempt parameters */
	si->acc_attempt.maxbusy_pgr = 10;
	si->acc_attempt.maxsztr_pgr = 10;
	si->acc_attempt.maxbusy_other = 10;
	si->acc_attempt.maxsztr_other = 10;

	/* registration ID */
	si->reg_id.regid = 1000;
}

void prepare_sysinfo(amps_si *si)
{
	int i = 0;

	si->type[i++] = SYSINFO_WORD1;
	si->type[i++] = SYSINFO_WORD2;
	si->type[i++] = SYSINFO_REG_ID;
	/* include only messages that differ from default */
	if (!!memcmp(&si->reg_incr, &default_reg_incr, sizeof(si->reg_incr)))
		si->type[i++] = SYSINFO_REG_INCR;
	if (!!memcmp(&si->loc_area, &default_loc_area, sizeof(si->loc_area)))
		si->type[i++] = SYSINFO_LOC_AREA;
	if (!!memcmp(&si->new_acc, &default_new_acc, sizeof(si->new_acc)))
		si->type[i++] = SYSINFO_NEW_ACC;
	if (!!memcmp(&si->overload, &default_overload, sizeof(si->overload)))
		si->type[i++] = SYSINFO_OVERLOAD;
	if (!!memcmp(&si->acc_type, &default_acc_type, sizeof(si->acc_type)))
		si->type[i++] = SYSINFO_ACC_TYPE;
	if (!!memcmp(&si->acc_attempt, &default_acc_attempt, sizeof(si->acc_attempt)))
		si->type[i++] = SYSINFO_ACC_ATTEMPT;
	si->num = i; /* train is running */
	si->count = 0; /* first message in train */
	if (i > (int)(sizeof(si->type) / sizeof(si->type[0]))) {
		fprintf(stderr, "si type array overflow, please fix!\n");
		abort();
	}
}

uint64_t get_sysinfo(amps_si *si, int debug)
{
	int count, nawc, end = 0;
	time_t ti = time(NULL);

	count = si->count;

	if (++si->count == si->num) {
		end = 1;
		si->num = 0; /* train is over */
	}

	switch (si->type[count]) {
	case SYSINFO_WORD1:
		nawc = si->num - 1;
		if (!tacs)
			return amps_encode_word1_system(si->dcc, si->word1.sid1, si->word1.ep, si->word1.auth, si->word1.pci, nawc, debug);
		else
			return tacs_encode_word1_system(si->dcc, si->word1.sid1, si->word1.ep, si->word1.auth, si->word1.pci, nawc, debug);
	case SYSINFO_WORD2:
		return amps_encode_word2_system(si->dcc, si->word2.s, si->word2.e, si->word2.regh, si->word2.regr, si->word2.dtx, si->word2.n_1, si->word2.rcf, si->word2.cpa, si->word2.cmax_1, end, debug);
	case SYSINFO_REG_ID:
		/* use time stamp to generate regid */
		si->reg_id.regid = ti & 0xfffff;
		return amps_encode_registration_id(si->dcc, si->reg_id.regid, end, debug);
	case SYSINFO_REG_INCR:
		return amps_encode_registration_increment(si->dcc, si->reg_incr.regincr, end, debug);
	case SYSINFO_LOC_AREA:
		return amps_encode_location_area(si->dcc, si->loc_area.pureg, si->loc_area.pdreg, si->loc_area.lreg, si->loc_area.locaid, end, debug);
	case SYSINFO_NEW_ACC:
		return amps_encode_new_access_channel_set(si->dcc, si->new_acc.newacc, end, debug);
	case SYSINFO_OVERLOAD:
		return amps_encode_overload_control(si->dcc, si->overload.olc, end, debug);
	case SYSINFO_ACC_TYPE:
		return amps_encode_access_type(si->dcc, si->acc_type.bis, si->acc_type.pci_home, si->acc_type.pci_roam, si->acc_type.bspc, si->acc_type.bscap, end, debug);
	case SYSINFO_ACC_ATTEMPT:
		return amps_encode_access_attempt(si->dcc, si->acc_attempt.maxbusy_pgr, si->acc_attempt.maxsztr_pgr, si->acc_attempt.maxbusy_other, si->acc_attempt.maxsztr_other, end, debug);
	}

	fprintf(stderr, "get_sysinfo unknown type, please fix!\n");
	abort();
}

