
/*
 *
 * webcam.c - part of Danovitsch Webcam
 *
 * Copyright (C) 2001 by Daan Vreeken
 *
 * Published under the terms of the GNU Public License 2.0
 * (or any later version)
 *
 */




#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <signal.h>
#include <stdio.h>

#include <math.h>

#include "general.h"
#include "io.h"
#include "display.h"
#include "capture.h"
#include "overlay.h"
#include "compress.h"
#include "server.h"
#include "http.h"
#include "webcam.h"
 


int			PleaseDie = 0;






int TooOld(struct timeval *Object, long Sec, long USec)
{
	struct timeval		Temp = *Object;
	struct timeval		Now;

	Temp.tv_sec+=Sec;
	Temp.tv_usec+=USec;
	while (Temp.tv_usec>=1000*1000)
	{
		Temp.tv_usec-=1000*1000;
		Temp.tv_sec++;
	}

	gettimeofday(&Now,NULL);

	if (Temp.tv_sec<Now.tv_sec)
		return 1;
	if ((Temp.tv_sec==Now.tv_sec) && (Temp.tv_usec<Now.tv_usec))
		return 1;
	return 0;
}



void DeadHandler(int Sig)
{
	if (Sig==SIGPIPE)
		if (Server_CurrentConnection!=NULL)
		{
			Debug(70,"Connection closed by foreign host. !!");
			Server_CurrentConnection->Close=1;
			return;
		}
		else
			Error("EEEK! Fixme!! Received SIGPIPE!!");

	PleaseDie=1;
}



void Translate(unsigned char *Dst, unsigned char *Src, int Bpp)
{
	int		X,Y;
	unsigned char	*A = Dst;
	unsigned char	*B = Src;
	long		C;
	int		Diff;
	int		Plus;
	unsigned char	Cnt;
	unsigned char	Cnt2;

	for (Y=0; Y<ImageHeight; Y++)
		for (X=0; X<ImageWidth; X++)
		{
			*A++=*B++;
			*A++=*B++;
			*A++=*B++;
			B++;
			if (Bpp==4) A++;
		}
}



void DrawOverlay(void)
{
	FILE			*OverlayTxt;
	char			Buf[100];
	char			Line[100];
	int			Y = 0;
	int			Len = 0;
	int			Readed = 1;
	int			Cnt;

	
	OverlayTxt=fopen("overlay.txt","r");
	
	if (!OverlayTxt) return;
	
	while (Readed>0)
	{
		Readed=fread(Buf,1,sizeof(Buf),OverlayTxt);
		
		for (Cnt=0; Cnt<Readed; Cnt++)
		{
			switch(Buf[Cnt])
			{
			case '\n':
				Line[Len]=0;
				Overlay_PrintF(7,14+Y*14,1,&White,&Black,Line);
				Y++;

				Len=0;
				break;
			default:
				Line[Len++]=Buf[Cnt];
			}
		}
		
	}
	Line[Len]=0;
	if (Len!=0)
		Overlay_PrintF(7,30+Y*14,1,&White,&Black,Line);
	
	fclose(OverlayTxt);
}



int main(void)
{
	XWnd			*Wnd;

	char			ListenAddr[20];
	struct sockaddr_in	Addr;
	int			Len;
	int			NewConn;
	int			FDMax;
	fd_set			ReadMask;
	fd_set			WriteMask;
	struct timeval		MaxWait;

	unsigned char		Nop;
	int			Cnt;

	struct Connection	*C;
	enum ConnState		State;

	struct Capture_SettingsArr	*Arr = &Capture_Settings[0];

	time_t			GMTime;
	struct tm		*Time;
	


	signal(SIGTERM,&DeadHandler);
	signal(SIGTSTP,&DeadHandler);
	signal(SIGABRT,&DeadHandler);
	signal(SIGQUIT,&DeadHandler);
	signal(SIGHUP,&DeadHandler);
	signal(SIGINT,&DeadHandler);

	signal(SIGPIPE,&DeadHandler);


	Display_Init();
	IO_Init();
	Server_Init();
	Compress_Init();


	Wnd=Display_CreateWindow();
	if (Wnd==NULL)
		ExitFatal("Could not create window!");
	
	Display_CreateImage(Wnd,ImageWidth,ImageHeight);
	Display_ResizeWindow(Wnd,ImageWidth+10,ImageHeight+10);


	pipe(&Capture_Fifo[0]);

	MaxWait.tv_sec=0;
	MaxWait.tv_usec=MainLoop_msec*1000;


	Capture_Device=open("/devs/bktr0",O_RDONLY);
	//Capture_Device=open("/dev/bktr1",O_RDONLY);
	if (Capture_Device==-1)
		ExitFatal("Could not open capture device!");


	Capture_SetFormat();
	Capture_SetSource();
	Capture_Setup();
	Capture_Single();


	///Set all settings to default setting..
	//
	while (Arr->ID!=NoID)
	{
		Capture_Set(Arr,NULL,Arr->NumValue,Arr->OrgValue);
		Arr++;
	}


	sprintf(ListenAddr,Server_ListenAddr);
	memset(&(Addr.sin_zero),0,8);
	Addr.sin_family=AF_INET;
	Addr.sin_port=htons(Server_Port);
	Addr.sin_addr.s_addr=inet_addr(ListenAddr);
	Len=sizeof(Addr);

	AcceptSock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	Cnt=1;
	setsockopt(AcceptSock,SOL_SOCKET,SO_REUSEADDR,(char *)&Cnt,sizeof(Cnt));

	if (bind(AcceptSock,(struct sockaddr *)&Addr,Len)==-1)
		ExitFatal("Could not bind socket!");

	if (listen(AcceptSock,5)==-1)
		ExitFatal("Socket error!");


	while (!PleaseDie)
	{
		/// Check age of frame buffer
		//
		if (TooOld(&FrameTime,FrameTimeout))
			Capture_Single();


		/// Dump dead connections
		//
		for (Cnt=0; Cnt<Clients; Cnt++)
			if (TooOld(&Client[Cnt].ConnectTime,ClientTimeout))
			{
				Debug(70,"Client timeout...");
				Client[Cnt].Close=1;
			}

		for (Cnt=Clients-1; Cnt>=0; Cnt--)
			if (Client[Cnt].Close)
				Server_CloseConnection(Cnt);


		FD_ZERO(&ReadMask);
		FD_ZERO(&WriteMask);
		FD_SET(AcceptSock,&ReadMask);
		FD_SET(Capture_Fifo[1],&ReadMask);

		FDMax=AcceptSock;
		if (Capture_Fifo[1]>FDMax) FDMax=Capture_Fifo[1];

		for (Cnt=0; Cnt<Clients; Cnt++)
		{
			State=Client[Cnt].State;

			if ((State==GetRequest) || (State==GetHeader) ||
				(State==GetData))
			{
				FD_SET(Client[Cnt].FD,&ReadMask);
				if (Client[Cnt].FD>FDMax)
					FDMax=Client[Cnt].FD;
			}

			if (State==Response)
			{
				FD_SET(Client[Cnt].FD,&WriteMask);
				if (Client[Cnt].FD>FDMax)
					FDMax=Client[Cnt].FD;
			}
		}


		/// Wait for data to get available (anywhere)
		//
		if (select(FDMax+1,&ReadMask,&WriteMask,NULL,&MaxWait)<=0)
		{
			continue;
		}


		/// New connection?
		//
		if (FD_ISSET(AcceptSock,&ReadMask))
		{
			NewConn=accept(AcceptSock,NULL,NULL);
			if (NewConn==-1)
				ExitFatal("Hugh? accept returned an error!");

			//fcntl(NewConn,F_SETFD,O_NONBLOCK);

			if (Clients<MaxClients)
			{
				C=&Client[Clients];
				C->FD=NewConn;
				C->Close=0;
				C->State=GetRequest;
				C->Mode=LineMode;
				C->Line=NULL;
				C->Length=0;
				C->Request=NULL;
				C->PostData=NULL;
				C->PostLength=0;
				C->ContentLength=0;
				C->Error=0;

				gettimeofday(&C->ConnectTime,NULL);
				Clients++;
			}
			else
			{
				Debug(80,"Server full... (dropping new connection)");
				close(NewConn);
			}
		}

		
		/// Check client connections for activity...
		//
		for (Cnt=0; Cnt<Clients; Cnt++)
		{
			C=&Client[Cnt];

			if (FD_ISSET(Client[Cnt].FD,&ReadMask))
				Server_HandleData(Cnt);

			if (FD_ISSET(Client[Cnt].FD,&WriteMask))
			{
				HTTP_Response(&Client[Cnt]);
				Client[Cnt].State=Zombie;
				Client[Cnt].Close=1;
			}
		}


		/// Capture done?
		//
		if (FD_ISSET(Capture_Fifo[1],&ReadMask))
		{
			read(Capture_Fifo[1],&Nop,1);

			memcpy(FrameBuffer,Frame,4*ImageWidth*ImageHeight);
			Overlay_PrintF(7,0,1,&White,&Black,"%s",Server_String);
			//Overlay_PrintF(7+(ImagesServed*10)%150,16,1,&White,&Black,"M o o ,    ] : 8 )");

			Overlay_PrintF(7,ImageHeight-32,1,&White,&Black,"Images served: %d",ImagesServed);
			Overlay_PrintF(7,ImageHeight-16,1,&White,&Black,"Viewpoint:%s",Capture_GetTextValue("InputF"));

			//Overlay_PrintF(90,70,1,&Red,&White,"Webcam is off...");
			//Overlay_PrintF(60,90,1,&Red,&White,"please come back later");
			
			//Overlay_PrintF(7,56,1,&White,&Black,"bliep");
			
			//Overlay_PrintF(7,40,1,&White,&Black,"ATV link test");
			//Overlay_PrintF(7,70,1,&White,&Black,"Link frequency: 1255MHz power: 30mW");
			//Overlay_PrintF(7,85,1,&White,&Black,"PA4DAN");
			
			DrawOverlay();
			
			time(&GMTime);
			Time=localtime(&GMTime);
			//Overlay_PrintF(7,ImageHeight-32,1,&White,&Black,"%02d:%02d.%02d (%s)",Time->tm_hour,Time->tm_min,Time->tm_sec,Time->tm_zone);
			Overlay_PrintF(150,ImageHeight-16,1,&White,&Black,"%02d:%02d.%02d (%s)",Time->tm_hour,Time->tm_min,Time->tm_sec,Time->tm_zone);

			Translate((unsigned char *)Wnd->Buffer,(unsigned char *)FrameBuffer,Wnd->Bpp);
			//memcpy(Wnd->Buffer,FrameBuffer,4*ImageWidth*ImageHeight);
			Display_UpdateImage(Wnd);
			NewFrame=0;
		}
	}

	Output("\n\nSomeone wants us to die!");

	Server_Shutdown();
	Capture_Shutdown();
	
	Output("\nMoo,\n]:8)");

	return 0;
}



