
 /*
  * this contains the routines used to upload or download a program from/to
  * the CP290's memory
  */

static struct TimerEvent_t {
	UBYTE te_Mode;
	UBYTE te_DOW;
	UBYTE te_Hour;
	UBYTE te_Minute;
	UBYTE te_UnitMap[2];
	UBYTE te_HouseCode;
	UBYTE te_LevelFunction;
} TimerTable[128];
static int TimerTablePos;

static struct GraphicsData_t {
	UBYTE gd_Graphic1;
	UBYTE gd_Graphic2;
} GraphicTable[256];
static int GraphicsTablePos;

#define DOWNLOAD_TEMPLATE "TIMER/S,GRAPHICS/S,NORMAL/S,SECURITY/S,TODAY/S,TOMORROW/S,EVERYDAY/S,WEEKENDS/S,WEEKDAYS/S,MON/S,TUE/S,WED/S,THU/S,FRI/S,SAT/S,SUN/S,HOUR/K/N,MINUTE/K/N,HOUSECODE/K,ON/S,OFF/S,DIM/K/N,SPECIALS/M/A"

enum DownloadArgs {
	ARG_TIMER,
	ARG_GRAPHICS,
	ARG_NORMAL,
	ARG_SECURITY,
	ARG_TODAY,
	ARG_TOMORROW,
	ARG_EVERYDAY,
	ARG_WEEKENDS,
	ARG_WEEKDAYS,
	ARG_MON,
	ARG_TUE,
	ARG_WED,
	ARG_THU,
	ARG_FRI,
	ARG_SAT,
	ARG_SUN,
	ARG_HOUR,
	ARG_MINUTE,
	ARG_HOUSECODE,
	ARG_ON,
	ARG_OFF,
	ARG_DIM,
	ARG_SPECIALS,
	ARG_sizeof
};

 /* prototypes for our private functions in this file */

static BOOL SendUploadRequest(BOOL RequestGraphics);
static UWORD ReadNextData(int DataLength, STRPTR Buffer);

 /* parse a file and download it to the CP290 */

void X10Download(STRPTR filename)
{
	BPTR fh;
	int index;
	UBYTE Buf[40];

	struct RDArgs *RArgs1, *RArgs2;
	STRPTR ArgArray[ARG_sizeof];

	STRPTR LineBuf;
	ULONG line;

	STRPTR *Specials;
	UWORD *uwordPtr;

	int houseCode;

	line = TimerTablePos = GraphicsTablePos = 0;

	if (LineBuf = AllocVec(BIG_BUF_SIZE, 0)) {
		if (fh = Open(filename, MODE_OLDFILE)) {
			while (FGets(fh, LineBuf, BIG_BUF_SIZE_BASE)) {
				line++;

				if (LineBuf[0] == '#' || LineBuf[0] == '\n')
					continue;

				strcpy(&LineBuf[strlen(LineBuf)], "\n");

				if (RArgs1 = AllocDosObject(DOS_RDARGS, TAG_DONE)) {
					RArgs1->RDA_Source.CS_Buffer = LineBuf;
					RArgs1->RDA_Source.CS_Length = strlen(LineBuf);
					RArgs1->RDA_Source.CS_CurChr = 0L;
					RArgs1->RDA_Flags |= RDAF_NOPROMPT;

					memset(ArgArray, 0, sizeof(ArgArray));

					RArgs2 = ReadArgs(DOWNLOAD_TEMPLATE, (LONG *) & ArgArray, RArgs1);

					if (RArgs2) {
						Specials = (STRPTR *) ArgArray[ARG_SPECIALS];

						if (ArgArray[ARG_TIMER]) {
							if (TimerTablePos < 128) {
								if (ArgArray[ARG_SECURITY])
									TimerTable[TimerTablePos].te_Mode = 9;
								else if (ArgArray[ARG_TODAY])
									TimerTable[TimerTablePos].te_Mode = 4;
								else if (ArgArray[ARG_TOMORROW])
									TimerTable[TimerTablePos].te_Mode = 2;
								else
									TimerTable[TimerTablePos].te_Mode = 8;

								TimerTable[TimerTablePos].te_DOW = 0;

								if (!ArgArray[ARG_TODAY] && !ArgArray[ARG_TOMORROW]) {
									if (ArgArray[ARG_EVERYDAY])
										TimerTable[TimerTablePos].te_DOW = 64 + 32 + 16 + 8 + 4 + 2 + 1;
									else if (ArgArray[ARG_WEEKENDS])
										TimerTable[TimerTablePos].te_DOW = 64 + 32;
									else if (ArgArray[ARG_WEEKDAYS])
										TimerTable[TimerTablePos].te_DOW = 16 + 8 + 4 + 2 + 1;

									if (ArgArray[ARG_SUN])
										TimerTable[TimerTablePos].te_DOW |= 64;
									if (ArgArray[ARG_SAT])
										TimerTable[TimerTablePos].te_DOW |= 32;
									if (ArgArray[ARG_FRI])
										TimerTable[TimerTablePos].te_DOW |= 16;
									if (ArgArray[ARG_THU])
										TimerTable[TimerTablePos].te_DOW |= 8;
									if (ArgArray[ARG_WED])
										TimerTable[TimerTablePos].te_DOW |= 4;
									if (ArgArray[ARG_TUE])
										TimerTable[TimerTablePos].te_DOW |= 2;
									if (ArgArray[ARG_MON])
										TimerTable[TimerTablePos].te_DOW |= 1;
								}

								if (ArgArray[ARG_HOUR])
									TimerTable[TimerTablePos].te_Hour = *((LONG *) ArgArray[ARG_HOUR]);
								else
									TimerTable[TimerTablePos].te_Hour = (UBYTE) - 1;

								if (ArgArray[ARG_MINUTE])
									TimerTable[TimerTablePos].te_Minute = *((LONG *) ArgArray[ARG_MINUTE]);
								else
									TimerTable[TimerTablePos].te_Minute = (UBYTE) - 1;

								houseCode = X10GetHC(*ArgArray[ARG_HOUSECODE]);
								if (houseCode != -1)
									TimerTable[TimerTablePos].te_HouseCode = HouseCode[houseCode];

								if (ArgArray[ARG_ON])
									TimerTable[TimerTablePos].te_LevelFunction = 2;
								else if (ArgArray[ARG_DIM])
									TimerTable[TimerTablePos].te_LevelFunction = 5 | ((*((LONG *) ArgArray[ARG_DIM]) - 1) << 4L);
								else
									TimerTable[TimerTablePos].te_LevelFunction = 3;

								TimerTable[TimerTablePos].te_UnitMap[0] = 0;
								TimerTable[TimerTablePos].te_UnitMap[1] = 0;

								while (*Specials) {
									index = (atol(*Specials++) - 1) & 0x0F;

									if (index < 8)
										TimerTable[TimerTablePos].te_UnitMap[0] |= 1L << (7 - index);
									else
										TimerTable[TimerTablePos].te_UnitMap[1] |= 1L << (7 - (index - 8));
								}

								if ((TimerTable[TimerTablePos].te_DOW || TimerTable[TimerTablePos].te_Mode == 4 || TimerTable[TimerTablePos].te_Mode == 2) &&
								    houseCode != -1 && (TimerTable[TimerTablePos].te_UnitMap[0] || TimerTable[TimerTablePos].te_UnitMap[1]) &&
								    TimerTable[TimerTablePos].te_Hour != -1 && TimerTable[TimerTablePos].te_Minute != -1)
									TimerTablePos++;
								else
									ErrorMsg(MSG_PARSE_ERROR, line, filename);
							}
							else
								ErrorMsg(MSG_TIMER_TABLE_FULL);

						}
						else if (ArgArray[ARG_GRAPHICS]) {
							if (Specials[0] && Specials[1] && !Specials[2]) {
								if (GraphicsTablePos < 256) {
									GraphicTable[GraphicsTablePos].gd_Graphic1 = atol(Specials[0]);
									GraphicTable[GraphicsTablePos++].gd_Graphic2 = atol(Specials[1]);
								}
								else
									ErrorMsg(MSG_GRAPHICS_TABLE_FULL);
							}
							else
								ErrorMsg(MSG_PARSE_ERROR, line, filename);
						}

						FreeArgs(RArgs2);
					}
					else
						ErrorMsg(MSG_PARSE_ERROR, line, filename);

					FreeDosObject(DOS_RDARGS, RArgs1);
				}
				else
					ErrorMsg(MSG_NO_RDARGS);
			}

			Close(fh);
		}
		else
			ErrorMsg(MSG_COULDNT_OPEN_FILE, filename);

		FreeVec(LineBuf);
	}
	else
		ErrorMsg(MSG_OUT_OF_MEMORY);

	if (TimerTablePos)
		for (index = 0; index < 128; index++) {
			memset(Buf, 0xFF, 16);
			Buf[16] = 3;

			uwordPtr = (UWORD *) & Buf[17];
			*uwordPtr = index * 8;
			Buf[19] = Buf[17];
			Buf[17] = Buf[18];
			Buf[18] = Buf[19];

			if (index < TimerTablePos)
				CopyMem((APTR) & TimerTable[index], &Buf[19], 8);
			else
				memset(&Buf[19], 0, 8);

			Buf[27] = Buf[19] + Buf[20] + Buf[21] + Buf[22] + Buf[23] + Buf[24] + Buf[25] + Buf[26];

			ClearSerial();
			SerWrite(Buf, 28);
			if (!X10WaitForAck(FALSE)) {
				ErrorMsg(MSG_CP290_ERROR_DOWNLOAD);
				return;
			}
		}

	if (GraphicsTablePos)
		for (index = 0; index < 256; index++) {
			memset(Buf, 0xFF, 16);
			Buf[16] = 3;

			uwordPtr = (UWORD *) & Buf[17];
			*uwordPtr = index * 2 + 1024;
			Buf[19] = Buf[17];
			Buf[17] = Buf[18];
			Buf[18] = Buf[19];

			if (index < GraphicsTablePos)
				CopyMem((APTR) & GraphicTable[index], &Buf[19], 2);
			else
				memset(&Buf[19], 0, 2);

			Buf[21] = Buf[19] + Buf[20];

			ClearSerial();
			SerWrite(Buf, 22);
			if (!X10WaitForAck(FALSE)) {
				ErrorMsg(MSG_CP290_ERROR_DOWNLOAD);
				return;
			}
		}
}

 /* upload the information stored in the interface and write it to a file */

void X10Upload(STRPTR filename)
{
	BPTR fh;
	int index, index2;
	UBYTE Buf[40];
	UBYTE CheckSum;
	UWORD raw;
	STRPTR outText;

	/* first try and download all the timer events */
	if (SendUploadRequest(FALSE)) {
		CheckSum = TimerTablePos = 0;

		for (index = 0; index < 128; index++) {
			raw = ReadNextData(8, (STRPTR) & TimerTable[TimerTablePos]);

			if (raw == TIMEOUT) {
				ErrorMsg(MSG_CP290_ERROR_READING, GetString(&LocaleInfo, MSG_TIMEOUT));
				return;
			}

			if (raw != EMPTY) {
				CheckSum = (CheckSum + raw) & 0xFF;
				TimerTablePos++;
			}
		}

		raw = SerGetRawChar(2);

		if (raw == TIMEOUT) {
			ErrorMsg(MSG_CP290_ERROR_READING, GetString(&LocaleInfo, MSG_TIMEOUT));
			return;
		}

		if (raw != CheckSum) {
			ErrorMsg(MSG_CP290_ERROR_READING, GetString(&LocaleInfo, MSG_CHECKSUM));
			return;
		}
	}

	/* next try and download all the graphics data */
	if (SendUploadRequest(TRUE)) {
		CheckSum = GraphicsTablePos = 0;

		for (index = 0; index < 256; index++) {
			raw = ReadNextData(2, (STRPTR) & GraphicTable[GraphicsTablePos]);

			if (raw == TIMEOUT) {
				ErrorMsg(MSG_CP290_ERROR_READING, GetString(&LocaleInfo, MSG_TIMEOUT));
				return;
			}

			if (raw != EMPTY) {
				CheckSum = (CheckSum + raw) & 0xFF;
				GraphicsTablePos++;
			}
		}

		raw = SerGetRawChar(2);

		if (raw == TIMEOUT) {
			ErrorMsg(MSG_CP290_ERROR_READING, GetString(&LocaleInfo, MSG_TIMEOUT));
			return;
		}

		if (raw != CheckSum) {
			ErrorMsg(MSG_CP290_ERROR_READING, GetString(&LocaleInfo, MSG_CHECKSUM));
			return;
		}
	}

	/* now try and write the data to the specified file */

	if (fh = Open(filename, MODE_NEWFILE)) {
		if (FPuts(fh, GetString(&LocaleInfo, MSG_COMMENT_TIMER))) {
			ErrorMsg(MSG_IO_ERROR, filename);
			Close(fh);
			return;
		}

		for (index = 0; index < TimerTablePos; index++) {
			if (FPuts(fh, "timer ")) {
				ErrorMsg(MSG_IO_ERROR, filename);
				Close(fh);
				return;
			}

			switch (TimerTable[index].te_Mode & 0x0F) {
			case 8:
				outText = "normal ";
				break;

			case 9:
				outText = "security ";
				break;

			case 4:
				outText = "today ";
				break;

			case 2:
				outText = "tomorrow ";
				break;

			default:
				outText = "";
				break;
			}

			if (FPuts(fh, outText)) {
				ErrorMsg(MSG_IO_ERROR, filename);
				Close(fh);
				return;
			}

			if (TimerTable[index].te_DOW == (64 + 32)) {
				if (FPuts(fh, "weekends ")) {
					ErrorMsg(MSG_IO_ERROR, filename);
					Close(fh);
					return;
				}
			}
			else if (TimerTable[index].te_DOW == (16 + 8 + 4 + 2 + 1)) {
				if (FPuts(fh, "weekdays ")) {
					ErrorMsg(MSG_IO_ERROR, filename);
					Close(fh);
					return;
				}
			}
			else if (TimerTable[index].te_DOW == (64 + 32 + 16 + 8 + 4 + 2 + 1)) {
				if (FPuts(fh, "everyday ")) {
					ErrorMsg(MSG_IO_ERROR, filename);
					Close(fh);
					return;
				}
			}
			else {
				if (TimerTable[index].te_DOW & 64)
					if (FPuts(fh, "sun ")) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}

				if (TimerTable[index].te_DOW & 1)
					if (FPuts(fh, "mon ")) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}

				if (TimerTable[index].te_DOW & 2)
					if (FPuts(fh, "tue ")) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}

				if (TimerTable[index].te_DOW & 4)
					if (FPuts(fh, "wed ")) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}

				if (TimerTable[index].te_DOW & 8)
					if (FPuts(fh, "thu ")) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}

				if (TimerTable[index].te_DOW & 16)
					if (FPuts(fh, "fri ")) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}

				if (TimerTable[index].te_DOW & 32)
					if (FPuts(fh, "sat ")) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}
			}

			sprintf(Buf, "hour %02ld ", TimerTable[index].te_Hour);
			if (FPuts(fh, Buf)) {
				ErrorMsg(MSG_IO_ERROR, filename);
				Close(fh);
				return;
			}

			sprintf(Buf, "minute %02ld ", TimerTable[index].te_Minute);
			if (FPuts(fh, Buf)) {
				ErrorMsg(MSG_IO_ERROR, filename);
				Close(fh);
				return;
			}

			switch (TimerTable[index].te_LevelFunction & 0x0F) {
			case 2:
				outText = "on";
				break;

			case 3:
				outText = "off";
				break;

			case 5:
				sprintf(Buf, "dim %ld", (TimerTable[index].te_LevelFunction >> 4L) + 1);
				outText = Buf;
				break;

			default:
				outText = "";
				break;
			}

			if (FPuts(fh, outText)) {
				ErrorMsg(MSG_IO_ERROR, filename);
				Close(fh);
				return;
			}

			for (index2 = 0; index2 < 16; index2++)
				if (HouseCode[index2] == TimerTable[index].te_HouseCode) {
					sprintf(Buf, " housecode %lc", index2 + 'A');
					if (FPuts(fh, Buf)) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}
				}

			for (index2 = 0; index2 < 16; index2++)
				if (TimerTable[index].te_UnitMap[index2 < 8 ? 0 : 1] & (1L << 7 - (index2 < 8 ? index2 : index2 - 8))) {
					sprintf(Buf, " %ld", index2 + 1);
					if (FPuts(fh, Buf)) {
						ErrorMsg(MSG_IO_ERROR, filename);
						Close(fh);
						return;
					}
				}

			FPutC(fh, '\n');
		}		/* for TimerEvent loop */

		sprintf(Buf, GetString(&LocaleInfo, MSG_COMMENT_GRAPHICS), TimerTablePos ? "\n" : "");
		if (FPuts(fh, Buf)) {
			ErrorMsg(MSG_IO_ERROR, filename);
			Close(fh);
			return;
		}

		for (index = 0; index < GraphicsTablePos; index++) {
			sprintf(Buf, "graphics %ld %ld\n", GraphicTable[index].gd_Graphic1, GraphicTable[index].gd_Graphic2);
			if (FPuts(fh, Buf)) {
				ErrorMsg(MSG_IO_ERROR, filename);
				Close(fh);
				return;
			}
		}

		Close(fh);
	}
	else
		ErrorMsg(MSG_CANT_CREATE_FILE, filename);
}

static BOOL SendUploadRequest(BOOL RequestGraphics)
{
	UBYTE Buf[17];
	UWORD raw;
	int index;

	memset(Buf, 0xFF, 16);
	Buf[16] = RequestGraphics ? 6 : 5;

	ClearSerial();
	SerWrite(Buf, 17);

	for (index = 0;;) {
		raw = SerGetRawChar(15);

		if (raw == TIMEOUT) {
			ErrorMsg(MSG_CP290_ERROR_NO_RESPONSE);
			return FALSE;
		}

		if (raw == 0xFF)
			index++;
		else if (index > 5)
			return TRUE;
	}
}

static UWORD ReadNextData(int DataLength, STRPTR Buffer)
{
	UWORD raw;
	UBYTE CheckSum;
	int index;

	raw = SerGetRawChar(2);

	if (raw == TIMEOUT)
		return TIMEOUT;

	if (raw == 0xFF)
		return EMPTY;
	else {			/* an event is stored here */
		Buffer[0] = CheckSum = raw;

		for (index = 1; index < DataLength; index++) {
			raw = SerGetRawChar(2);

			if (raw == TIMEOUT)
				return TIMEOUT;

			Buffer[index] = raw;
			CheckSum += raw;
		}

		return (UWORD) CheckSum;
	}
}
