/*
**	TimeDate.c
**
**	Phone rate calculation support routines
**
**	Copyright © 1990-1996 by Olaf `Olsen' Barthel
**		All Rights Reserved
**
** :ts=4
*/

#ifndef _GLOBAL_H
#include "Global.h"
#endif

	/* SelectTime():
	 *
	 *	Searches for the correct unit/pay conversion values.
	 */

VOID
SelectTime(struct PhoneEntry *SomeEntry,struct List *List,struct timeval *TimeVal)
{
	struct TimeDateNode	*TimeDateNode;
	struct ClockData	 ClockData;
	LONG				 Time;
	BOOL				 FoundOne = FALSE;
	LONG				 i,Last;

		/* If we get a specific time of day, use it. */

	if(TimeVal)
		Amiga2Date(TimeVal->tv_secs,&ClockData);
	else
	{
		struct timeval Now;

			/* Obtain current time and date. */

		GetSysTime(&Now);

			/* Convert into a more suitable form (note: seconds are
			 * required as an input value, ice is extra).
			 */

		Amiga2Date(Now.tv_secs,&ClockData);
	}

		/* Apparently, in the US of A the week starts with Sunday, we'll
		 * wrap the week around to let it start with Monday.
		 */

	if(ClockData . wday)
		ClockData . wday--;
	else
		ClockData . wday = 6;

		/* Change the month, too... */

	ClockData . month--;

	if(List)
	{
		if(!List->lh_Head->ln_Succ)
			List = NULL;
	}

		/* If no special list to search is given use the phonebook entry. */

	if(!List)
	{
		if(SomeEntry && !SomeEntry->Header->NoRates)
			List = (struct List *)&SomeEntry->TimeDateList;
		else
		{
			PayPerUnit[DT_FIRST_UNIT]	= 0;
			PayPerUnit[DT_NEXT_UNIT]	= 0;
			SecPerUnit[DT_FIRST_UNIT]	= 0;
			SecPerUnit[DT_NEXT_UNIT]	= 0;

			return;
		}
	}

	if(!List->lh_Head->ln_Succ)
	{
		PayPerUnit[DT_FIRST_UNIT]	= 0;
		PayPerUnit[DT_NEXT_UNIT]	= 0;
		SecPerUnit[DT_FIRST_UNIT]	= 0;
		SecPerUnit[DT_NEXT_UNIT]	= 0;

		return;
	}

		/* First step: search for current day. */

	TimeDateNode = (struct TimeDateNode *)List->lh_Head;

		/* Skip first entry. */

	TimeDateNode = (struct TimeDateNode *)TimeDateNode->VanillaNode . ln_Succ;

		/* First step: search for date settings. */

	while(TimeDateNode->VanillaNode . ln_Succ)
	{
			/* Does it match a specific date? */

		if(TimeDateNode->Header . Month == ClockData . month && TimeDateNode->Header . Day == ClockData . mday)
		{
			FoundOne = TRUE;

			break;
		}

		TimeDateNode = (struct TimeDateNode *)TimeDateNode->VanillaNode . ln_Succ;
	}

		/* Second step: search for week day settings. */

	if(!FoundOne)
	{
		TimeDateNode = (struct TimeDateNode *)List->lh_Head;

			/* Skip first entry. */

		TimeDateNode = (struct TimeDateNode *)TimeDateNode->VanillaNode . ln_Succ;

		while(TimeDateNode->VanillaNode . ln_Succ)
		{
				/* Does it match a specific day? */

			if(TimeDateNode->Header . Month == -1 && (TimeDateNode->Header . Day & (1L << ClockData . wday)))
			{
				FoundOne = TRUE;

				break;
			}

			TimeDateNode = (struct TimeDateNode *)TimeDateNode->VanillaNode . ln_Succ;
		}
	}

		/* Third step: use default settings. */

	if(!FoundOne)
		TimeDateNode = (struct TimeDateNode *)List->lh_Head;

		/* Convert current time to packed format. */

	Time = DT_GET_TIME(ClockData . hour,ClockData . min);

		/* Start with a blank. */

	Last = -1;

		/* Look for fitting time. */

	for(i = 0 ; i < TimeDateNode->Table[0] . Count ; i++)
	{
			/* The time we are looking for must be >= the
			 * current time.
			 */

		if(TimeDateNode->Table[i] . Time > Time)
		{
			if(i == 0)
				break;
			else
			{
				Last = i - 1;
				break;
			}
		}
		else
			continue;
	}

		/* If none is found, take the last one in the list.
		 * Note that this requires the list to be sorted in
		 * ascending order.
		 */

	if(Last == -1)
		Last = TimeDateNode->Table[0] . Count - 1;

		/* Fill in the remaining data. */

	PayPerUnit[DT_FIRST_UNIT]	= TimeDateNode->Table[Last] . PayPerUnit[DT_FIRST_UNIT];
	PayPerUnit[DT_NEXT_UNIT]	= TimeDateNode->Table[Last] . PayPerUnit[DT_NEXT_UNIT];
	SecPerUnit[DT_FIRST_UNIT]	= TimeDateNode->Table[Last] . SecPerUnit[DT_FIRST_UNIT];
	SecPerUnit[DT_NEXT_UNIT]	= TimeDateNode->Table[Last] . SecPerUnit[DT_NEXT_UNIT];
}

struct List *
FindTimeDate(struct List *Patterns,STRPTR Number)
{
	struct PatternNode	*Node;
	UBYTE				 Dst[516];

	if(Kick30)
	{
		for(Node = (struct PatternNode *)Patterns->lh_Head ; Node->Node . ln_Succ ; Node = (struct PatternNode *)Node->Node . ln_Succ)
		{
			if(ParsePatternNoCase(Node->Pattern,Dst,516) != -1)
			{
				if(MatchPatternNoCase(Dst,Number))
					return(&Node->List);
			}
		}
	}
	else
	{
		UBYTE	Src[256];
		STRPTR	Index;

		for(Node = (struct PatternNode *)Patterns->lh_Head ; Node->Node . ln_Succ ; Node = (struct PatternNode *)Node->Node . ln_Succ)
		{
			strcpy(Index = Src,Node->Pattern);

			while(*Index)
			{
				*Index = ToUpper(*Index);

				Index++;
			}

			if(ParsePatternNoCase(Src,Dst,516) != -1)
			{
				if(MatchPatternNoCase(Dst,Number))
					return(&Node->List);
			}
		}
	}

	return(NULL);
}

VOID
DeletePatternNode(struct PatternNode *Pattern)
{
	if(Pattern)
	{
		struct TimeDateNode	*Node,
							*NextNode;

		for(Node = (struct TimeDateNode *)Pattern->List . lh_Head ; NextNode = (struct TimeDateNode *)Node->VanillaNode . ln_Succ ; Node = NextNode)
			FreeTimeDateNode(Node);

		FreeVecPooled(Pattern);
	}
}

struct PatternNode *
CreatePatternNode(STRPTR Comment)
{
	struct PatternNode *Node;

	if(Node = (struct PatternNode *)AllocVecPooled(sizeof(struct PatternNode),MEMF_ANY | MEMF_CLEAR))
	{
		struct TimeDateNode *TimeDateNode;

		Node->Node . ln_Name = Node->Comment;

		strcpy(Node->Comment,Comment);

		NewList(&Node->List);

		if(TimeDateNode = CreateTimeDateNode(-1,-1,"",2))
		{
			AddTail(&Node->List,&TimeDateNode->VanillaNode);

			return(Node);
		}

		FreeVecPooled(Node);
	}

	return(NULL);
}

VOID
DeletePatternList(struct List *List)
{
	if(List)
	{
		FreePatternList(List);

		FreeVecPooled(List);
	}
}

VOID
FreePatternList(struct List *List)
{
	if(List)
	{
		struct PatternNode	*Pattern,
							*NextPattern;

		for(Pattern = (struct PatternNode *)List->lh_Head ; NextPattern = (struct PatternNode *)Pattern->Node . ln_Succ ; Pattern = NextPattern)
			DeletePatternNode(Pattern);

		NewList(List);
	}
}

VOID
ConvertTimeDate(struct TimeDateOld *Old,struct TimeDate *New)
{
	memset(New,0,sizeof(struct TimeDate));

	New->Count			= Old->Count;
	New->PayPerUnit[0]	= ((ULONG)Old->PayPerUnit[0]) * 10000;
	New->PayPerUnit[1]	= ((ULONG)Old->PayPerUnit[1]) * 10000;
	New->SecPerUnit[0]	= ((ULONG)Old->SecPerUnit[0]) * 10000;
	New->SecPerUnit[1]	= ((ULONG)Old->SecPerUnit[1]) * 10000;
	New->Time			= Old->Time;
	New->Mark			= FALSE;
}

struct List *
LoadTimeDateList(STRPTR Name,LONG *Error)
{
	struct IFFHandle	*Handle;
	struct List			*List = NULL;

	*Error = 0;

	if(Handle = (struct IFFHandle *)AllocIFF())
	{
		if(Handle->iff_Stream = Open(Name,MODE_OLDFILE))
		{
			InitIFFasDOS(Handle);

			if(!(*Error = OpenIFF(Handle,IFFF_READ)))
			{
				STATIC LONG Stops[4 * 2] =
				{
					ID_TERM, ID_VERS,
					ID_TERM, ID_NAME,
					ID_TERM, ID_DAT2,
					ID_TERM, ID_DATE
				};

				struct ContextNode *Chunk;

				if(!(*Error = StopChunks(Handle,Stops,4)))
				{
					if(List = (struct List *)AllocVecPooled(sizeof(struct MinList),MEMF_ANY))
					{
						struct PatternNode *Pattern = NULL;

						NewList(List);

						while(!ParseIFF(Handle,IFFPARSE_SCAN))
						{
							Chunk = CurrentChunk(Handle);

							if(Chunk->cn_ID == ID_NAME)
							{
								if(Pattern = (struct PatternNode *)AllocVecPooled(sizeof(struct PatternNode),MEMF_ANY | MEMF_CLEAR))
								{
									if(ReadChunkBytes(Handle,Pattern->Pattern,Chunk->cn_Size) == Chunk->cn_Size)
									{
										NewList(&Pattern->List);

										Pattern->Node . ln_Name = Pattern->Comment;

										AddTail(List,Pattern);
									}
									else
									{
										*Error = IoErr();

										FreeVecPooled(Pattern);

										break;
									}
								}
								else
								{
									*Error = ERR_NO_MEM;

									break;
								}
							}

							if(Chunk->cn_ID == ID_DATE)
							{
								struct TimeDateNode	*Node;
								LONG				 Count = (Chunk->cn_Size - sizeof(struct TimeDateHeader)) / sizeof(struct TimeDateOld);

								if(!Pattern)
								{
									*Error = IFFERR_MANGLED;

									break;
								}

								if(Node = CreateTimeDateNode(-1,-1,"",Count))
								{
									struct TimeDateOld *Old;

									if(Old = (struct TimeDateOld *)AllocVecPooled(sizeof(struct TimeDateOld) * Count,MEMF_ANY))
									{
										if(ReadChunkBytes(Handle,&Node->Header,sizeof(struct TimeDateHeader)) == sizeof(struct TimeDateHeader))
										{
											if(ReadChunkRecords(Handle,Old,sizeof(struct TimeDateOld),Count) == Count)
											{
												LONG i;

												for(i = 0 ; i < Count ; i++)
													ConvertTimeDate(&Old[i],&Node->Table[i]);

												AdaptTimeDateNode(Node);

												AddTail(&Pattern->List,&Node->VanillaNode);

												FreeVecPooled(Old);
											}
											else
											{
												*Error = IoErr();

												FreeTimeDateNode(Node);
												FreeVecPooled(Old);

												break;
											}
										}
										else
										{
											*Error = IoErr();

											FreeTimeDateNode(Node);
											FreeVecPooled(Old);

											break;
										}
									}
								}
								else
								{
									*Error = ERROR_NO_FREE_STORE;
									break;
								}
							}

							if(Chunk->cn_ID == ID_DAT2)
							{
								struct TimeDateNode	*Node;
								LONG				 Count = (Chunk->cn_Size - sizeof(struct TimeDateHeader)) / sizeof(struct TimeDate);

								if(!Pattern)
								{
									*Error = IFFERR_MANGLED;

									break;
								}

								if(Node = CreateTimeDateNode(-1,-1,"",Count))
								{
									if(ReadChunkBytes(Handle,&Node->Header,sizeof(struct TimeDateHeader)) == sizeof(struct TimeDateHeader))
									{
										if(ReadChunkRecords(Handle,Node->Table,sizeof(struct TimeDate),Count) == Count)
										{
											AdaptTimeDateNode(Node);

											AddTail(&Pattern->List,&Node->VanillaNode);
										}
										else
										{
											*Error = IoErr();

											FreeTimeDateNode(Node);

											break;
										}
									}
									else
									{
										*Error = IoErr();

										FreeTimeDateNode(Node);

										break;
									}
								}
								else
								{
									*Error = ERROR_NO_FREE_STORE;
									break;
								}
							}
						}
					}
				}

				CloseIFF(Handle);
			}

			Close(Handle->iff_Stream);
		}
		else
			*Error = IoErr();

		FreeIFF(Handle);
	}
	else
		*Error = ERR_NO_MEM;

	if(*Error)
	{
		DeletePatternList(List);

		return(NULL);
	}
	else
		return(List);
}

BOOL
SaveTimeDateList(STRPTR Name,struct List *List,LONG *Error)
{
	*Error = 0;

#ifdef BETA
	if(ShowRequest(Window,
		"ONE WAY TICKET WARNING!\n\n"
		"The file format written by this `term' beta test release\n"
		"cannot be read by older program releases. And just as it\n"
		"happens, not even the author of these lines can guarantee\n"
		"you that this very program will be able to read the files\n"
		"back it writes.",
	"Uh oh...|I know what I'm doing"))
		return(TRUE);
#endif

	if(List->lh_Head->ln_Succ)
	{
		struct IFFHandle *Handle;

		if(Handle = (struct IFFHandle *)AllocIFF())
		{
			BOOL Created;

			if(Handle->iff_Stream = Open(Name,MODE_NEWFILE))
			{
				Created = TRUE;

				InitIFFasDOS(Handle);

				if(!(*Error = OpenIFF(Handle,IFFF_WRITE)))
				{
					if(!(*Error = PushChunk(Handle,ID_TERM,ID_FORM,IFFSIZE_UNKNOWN)))
					{
						if(!(*Error = PushChunk(Handle,0,ID_VERS,IFFSIZE_UNKNOWN)))
						{
							struct TermInfo TermInfo;

							TermInfo . Version	= CONFIG_FILE_VERSION;
							TermInfo . Revision	= CONFIG_FILE_REVISION;

							if(WriteChunkRecords(Handle,&TermInfo,sizeof(struct TermInfo),1) == 1)
							{
								if(!(*Error = PopChunk(Handle)))
								{
									struct PatternNode *Node;

									for(Node = (struct PatternNode *)List->lh_Head ; !(*Error) && Node->Node . ln_Succ ; Node = (struct PatternNode *)Node->Node . ln_Succ)
									{
										if(!(*Error = PushChunk(Handle,0,ID_NAME,296)))
										{
											if(WriteChunkBytes(Handle,Node->Pattern,296) == 296)
											{
												if(!(*Error = PopChunk(Handle)))
												{
													struct TimeDateNode *TimeDateNode;

													for(TimeDateNode = (struct TimeDateNode *)Node->List . lh_Head ; TimeDateNode->VanillaNode . ln_Succ ; TimeDateNode = (struct TimeDateNode *)TimeDateNode->VanillaNode . ln_Succ)
													{
														if(!(*Error = PushChunk(Handle,0,ID_DAT2,sizeof(struct TimeDateHeader) + TimeDateNode->Table[0] . Count * sizeof(struct TimeDate))))
														{
															if(WriteChunkBytes(Handle,&TimeDateNode->Header,sizeof(struct TimeDateHeader)) != sizeof(struct TimeDateHeader))
															{
																*Error = IoErr();

																break;
															}
															else
															{
																if(WriteChunkBytes(Handle,TimeDateNode->Table,TimeDateNode->Table[0] . Count * sizeof(struct TimeDate)) != TimeDateNode->Table[0] . Count * sizeof(struct TimeDate))
																{
																	*Error = IoErr();

																	break;
																}
																else
																{
																	if(*Error = PopChunk(Handle))
																		break;
																}
															}
														}
														else
															break;
													}
												}
											}
											else
												*Error = IoErr();
										}
									}
								}
							}
						}

						if(!(*Error))
							*Error = PopChunk(Handle);
					}

					CloseIFF(Handle);
				}

				Close(Handle->iff_Stream);
			}
			else
			{
				*Error = IoErr();

				Created = FALSE;
			}

			if(Created)
			{
				if(*Error)
					DeleteFile(Name);
				else
					AddProtection(Name,FIBF_EXECUTE);
			}

			FreeIFF(Handle);
		}
		else
			*Error = ERR_NO_MEM;
	}

	if(*Error)
		return(FALSE);
	else
		return(TRUE);
}
