/* Samsung-specific functions
 * Copyright (C) 2004 Claudio Matsuoka <cmatsuoka@gmail.com>
 * Tested with S300 only!
 */

#include <gammu-config.h>

#ifdef GSM_ENABLE_ATGEN

#include <string.h>
#include <time.h>
#include <ctype.h>

#include "../../misc/coding/coding.h"
#include "../../gsmcomon.h"
#include "../pfunc.h"

#include "atgen.h"
#include "samsung.h"

/* Binary frame size */
#define BLKSZ 1024

struct ModelRes {
	char *model;
	int width;
	int height;
};

static struct ModelRes modres[] = {
	{ "S100", 128, 128 },
	{ "S200", 128, 113 },
	{ "S300", 128,  97 },
	{ "S500", 128, 128 },
	{ "T100", 128, 128 },
	{ "E700", 128, 128 },
	{ NULL, 0, 0 }
};

/*
 * CRC functions from the Granch SBNI12 Linux driver by
 * Denis I. Timofeev <timofeev@granch.ru>
 */
static unsigned int crc32tab[] = {
	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605,
	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C,
	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53,
	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A,
	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661,
	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278,
	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF,
	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6,
	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD,
	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4,
	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B,
	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82,
	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9,
	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0,
	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7,
	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE,
	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795,
	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C,
	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3,
	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA,
	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1,
	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8,
	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F,
	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76,
	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D,
	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344,
	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B,
	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12,
	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739,
	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320,
	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17,
	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E,
	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525,
	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C,
	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73,
	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A,
	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541,
	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158,
	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF,
	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6,
	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED,
	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4,
	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB,
	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2,
	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589,
	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190,
	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87,
	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E,
	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5,
	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC,
	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3,
	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA,
	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1,
	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8,
	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F,
	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856,
	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D,
	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064,
	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B,
	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832,
	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419,
	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000
};

static unsigned int GetCRC(char *data, int size)
{
	unsigned int crc = 0;

	while (size--)
		crc = crc32tab[(crc ^ *data++) & 0xff] ^ ((crc >> 8) & 0x00FFFFFF);

	return crc;
}

/*
 * Frame transfer
 */

static GSM_Error WaitFor(GSM_StateMachine *s, char *t, int ttl)
{
	char 		readbuf[100];
	int 		n;
	unsigned int 	sec;
        GSM_DateTime    Date;

        GSM_GetCurrentDateTime (&Date);
        sec = Date.Second;

	n = s->Device.Functions->ReadDevice(s, readbuf, 80);
	readbuf[n] = 0;
	while (strstr(readbuf, t) == NULL && (sec + ttl) >= Date.Second) {
		my_sleep(5000);
		n = s->Device.Functions->ReadDevice(s, readbuf, 80);
		readbuf[n] = 0;
        	GSM_GetCurrentDateTime (&Date);
	}

	return (sec + ttl) >= Date.Second ? ERR_NONE : ERR_TIMEOUT;
}

static GSM_Error SetSamsungFrame(GSM_StateMachine *s, unsigned char *buff, int size, GSM_Phone_RequestID id)
{
	GSM_Phone_Data		*Phone = &s->Phone.Data;
	GSM_Error		error;
	int			i, count;

	count = size / BLKSZ;

	for (i = 0; i < count; i++) {
		error = WaitFor(s, ">", 4);
 		if (error!=ERR_NONE) return error;

 		error = s->Protocol.Functions->WriteMessage(s,
			buff + i * BLKSZ, BLKSZ, 0x00);
 		if (error!=ERR_NONE) return error;
	}

	error = WaitFor(s, ">", 4);
 	if (error!=ERR_NONE) return error;
	error = s->Protocol.Functions->WriteMessage(s,
		buff + i * BLKSZ, size%BLKSZ, 0x00);
	if (error!=ERR_NONE) return error;

	error = GSM_WaitFor(s, "", 0, 0x00, 4, id);
	if (error!=ERR_NONE) return error;

	return Phone->DispatchError;
}

/* Answer format for binary data transfer
 *
 * SDNDCRC = 0xa : RECEIVECRC = 0xcbf53a1c : BINSIZE = 5
 * CRCERR
 */
static GSM_Error ReplySetSamsungFrame(GSM_Protocol_Message msg, GSM_StateMachine *s)
{
	unsigned long 		txcrc, rxcrc;
	int 			binsize;
	char 			*pos;

	/* Parse SDNDCRC */
	pos = strchr(msg.Buffer, '=');
	if (!pos) return ERR_UNKNOWN;
	pos++;
	txcrc = strtoul(pos, NULL, 0);
	smprintf(s, "Sent CRC     : 0x%lx\n", txcrc);

	/* Parse RECEIVECRC */
	pos = strchr(pos, '=');
	if (!pos) return ERR_UNKNOWN;
	pos++;
	rxcrc = strtoul(pos, NULL, 0);
	smprintf(s, "Reveived CRC : 0x%lx\n", rxcrc);

	/* Parse BINSIZE */
	pos = strchr(pos, '=');
	if (!pos) return ERR_UNKNOWN;
	pos++;
	binsize = strtoul(pos, NULL, 0);
	smprintf(s, "Binary size  : %d\n", binsize);

	return txcrc == rxcrc ? ERR_NONE : ERR_WRONGCRC;
}

/*
 * Bitmaps
 */

GSM_Error SAMSUNG_ReplyGetBitmap(GSM_Protocol_Message msg, GSM_StateMachine *s)
{
 	GSM_Phone_ATGENData 	*Priv = &s->Phone.Data.Priv.ATGEN;
	unsigned char 		buffer[32];
	char 			*pos;
	int			location, count;

 	switch (Priv->ReplyState) {
 	case AT_Reply_OK:
		smprintf(s, "Bitmap info received\n");
		/* Parse +IMGR:location,name,0,0,0,0 */

		/* Parse location */
		pos = strchr(msg.Buffer, ':');
		if (!pos) return ERR_UNKNOWN;
		pos++;
		location = atoi(pos);
		smprintf(s, "Location : %d\n", location);

		/* Parse name */
		pos = strchr(pos, '"');
		if (!pos) return ERR_UNKNOWN;
		pos++;
		for (count = 0; count < 31; count++) {
			if (pos[count] == '"')
				break;
			buffer[count] = pos[count];
		}
		buffer[count] = 0;
		smprintf(s, "Name     : %s\n", buffer);
		s->Phone.Data.Bitmap->Name = malloc((strlen(buffer) + 1) * 2);
		if (s->Phone.Data.Bitmap->Name == NULL)
			return ERR_MOREMEMORY;
		EncodeUnicode(s->Phone.Data.Bitmap->Name, buffer, strlen(buffer));

		s->Phone.Data.Bitmap->Location = location;

		return ERR_NONE;
	case AT_Reply_Error:
		return ERR_NOTSUPPORTED;
	case AT_Reply_CMSError:
	        return ATGEN_HandleCMSError(s);
	case AT_Reply_CMEError:
	        return ATGEN_HandleCMEError(s);
 	default:
		return ERR_UNKNOWNRESPONSE;
	}
}

GSM_Error SAMSUNG_ReplySetBitmap(GSM_Protocol_Message msg, GSM_StateMachine *s)
{
	smprintf(s, "Bitmap sent\n");
	return ReplySetSamsungFrame(msg, s);
}

GSM_Error SAMSUNG_GetBitmap(GSM_StateMachine *s, GSM_Bitmap *Bitmap)
{
	unsigned char req[100];

	s->Phone.Data.Bitmap=Bitmap;
	smprintf(s, "Getting bitmap\n");
	sprintf(req, "AT+IMGR=%d\r", Bitmap->Location-1);
	return GSM_WaitFor (s, req, strlen(req), 0x00, 4, ID_GetBitmap);
}

GSM_Error SAMSUNG_SetBitmap(GSM_StateMachine *s, GSM_Bitmap *Bitmap)
{
	unsigned char	req[100];
	unsigned long	crc;
	GSM_Error	error;
	char		name[50], *dot, *model;
	GSM_Phone_Data  *Data = &s->Phone.Data;
	int 		i;

	s->Phone.Data.Bitmap = Bitmap;
	smprintf(s, "Setting bitmap\n");

	if (Bitmap->Type != GSM_PictureBinary) {
		smprintf(s, "Invalid picture type\n");
		return ERR_INVALIDDATA;
	}

	if (Bitmap->BinaryPic.Type != PICTURE_GIF) {
		smprintf(s, "Invalid binary picture type\n");
		return ERR_INVALIDDATA;
	}

	/* Check if picture size matches phone model */
	model = Data->ModelInfo->model;
	smprintf(s, "Checking picture size for %s\n", model);
	for (i = 0; modres[i].model; i++) {
		if (!strcmp(model, modres[i].model)) {
			if (Bitmap->BitmapWidth != modres[i].width ||
			    Bitmap->BitmapHeight != modres[i].height) {
				smprintf(s, "Model %s must use %d x %d picture size\n",
					modres[i].model, modres[i].width,
					modres[i].height);
				return ERR_INVALIDDATA;
			}
			break;
		}
	}
	if (modres[i].model == NULL) {
		smprintf(s, "Model \"%s\" is not supported.\n", Data->Model);
		return ERR_NOTSUPPORTED;
	}

	crc = GetCRC(Bitmap->BinaryPic.Buffer, Bitmap->BinaryPic.Length);

	/* Remove extension from file name */
	strncpy(name, DecodeUnicodeString(Bitmap->Name), 50);
	if ((dot = strrchr(name, '.')) != NULL)
		*dot = 0;

	sprintf(req, "AT+IMGW=0,\"%s\",2,0,0,0,0,100,%d,%u\r", name,
		Bitmap->BinaryPic.Length, (unsigned int)crc);

	error = s->Protocol.Functions->WriteMessage(s, req, strlen(req), 0x00);
	if (error!=ERR_NONE) return error;

	return SetSamsungFrame(s, Bitmap->BinaryPic.Buffer,
		Bitmap->BinaryPic.Length, ID_SetBitmap);
}

/*
 * Ringtones
 */

GSM_Error SAMSUNG_ReplyGetRingtone(GSM_Protocol_Message msg, GSM_StateMachine *s)
{
 	GSM_Phone_ATGENData 	*Priv = &s->Phone.Data.Priv.ATGEN;
	unsigned char 		buffer[32];
	char 			*pos;
	int			location, length, count;

 	switch (Priv->ReplyState) {
 	case AT_Reply_OK:
		smprintf(s, "Ringtone info received\n");
		/* Parse +MELR:location,name,size */

		/* Parse location */
		pos = strchr(msg.Buffer, ':');
		if (!pos) return ERR_UNKNOWN;
		pos++;
		location = atoi(pos);
		smprintf(s, "Location : %d\n", location);

		/* Parse name */
		pos = strchr(pos, '"');
		if (!pos) return ERR_UNKNOWN;
		pos++;
		/* Ringtone.Name size is 20 chars */
		for (count = 0; count < 19; count++) {
			if (pos[count] == '"')
				break;
			buffer[count] = pos[count];
		}
		buffer[count] = 0;
		smprintf(s, "Name     : %s\n", buffer);
		EncodeUnicode(s->Phone.Data.Ringtone->Name,buffer,strlen(buffer));

		/* Parse ringtone length */
		pos = strchr(pos, ',');
		if (!pos) return ERR_UNKNOWN;
		pos++;
		length = atoi(pos);
		smprintf(s, "Length   : %d\n", length);

		/* S300 ringtones are always MMF */
		s->Phone.Data.Ringtone->Format = RING_MMF;
		s->Phone.Data.Ringtone->Location = location;
		s->Phone.Data.Ringtone->BinaryTone.Length = length;

		return ERR_NONE;
	case AT_Reply_Error:
		return ERR_UNKNOWN;
	case AT_Reply_CMSError:
	        return ATGEN_HandleCMSError(s);
	case AT_Reply_CMEError:
	        return ATGEN_HandleCMEError(s);
 	default:
		return ERR_UNKNOWNRESPONSE;
	}
}

GSM_Error SAMSUNG_GetRingtone(GSM_StateMachine *s, GSM_Ringtone *Ringtone, bool PhoneRingtone UNUSED)
{
	unsigned char req[100];

	s->Phone.Data.Ringtone = Ringtone;
	smprintf(s, "Getting ringtone\n");
	sprintf(req, "AT+MELR=%d\r", Ringtone->Location-1);
	return GSM_WaitFor (s, req, strlen(req), 0x00, 4, ID_GetRingtone);
}

GSM_Error SAMSUNG_ReplySetRingtone(GSM_Protocol_Message msg, GSM_StateMachine *s)
{
	smprintf(s, "Ringtone sent\n");
	return ReplySetSamsungFrame(msg, s);
}

GSM_Error SAMSUNG_SetRingtone(GSM_StateMachine *s, GSM_Ringtone *Ringtone, int *maxlength UNUSED)
{
	unsigned char	req[100];
	unsigned long	crc;
	GSM_Error	error;
	char		name[50], *dot;

	s->Phone.Data.Ringtone = Ringtone;
	smprintf(s, "Setting ringtone\n");

	if (Ringtone->Format != RING_MMF) {
		smprintf(s, "Not MMF ringtone\n");
		return ERR_INVALIDDATA;
	}

	/* Remove extension from file name */
	strncpy(name, DecodeUnicodeString(Ringtone->Name), 50);
	if ((dot = strrchr(name, '.')) != NULL) *dot = 0;

	crc = GetCRC(Ringtone->BinaryTone.Buffer, Ringtone->BinaryTone.Length);
	sprintf(req, "AT+MELW=0,\"%s\",4," SIZE_T_FORMAT ",%u\r", name,
		Ringtone->BinaryTone.Length, (unsigned int)crc);

	error = s->Protocol.Functions->WriteMessage(s, req, strlen(req), 0x00);
	if (error!=ERR_NONE) return error;

	return SetSamsungFrame(s, Ringtone->BinaryTone.Buffer,
		Ringtone->BinaryTone.Length, ID_SetRingtone);
}

#endif
