#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <dos/dos.h>
#include <exec/exec.h>
#include <devices/serial.h>
#include <jpeg/jpeg.h>
#include <cybergraphics/cybergraphics.h>
#include <intuition/intuition.h>
#include <libraries/asl.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <clib/jpeg_protos.h>
#include <clib/cybergraphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/asl_protos.h>

#include <pragma/dos_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/jpeg_lib.h>
#include <pragma/cybergraphics_lib.h>
#include <pragma/intuition_lib.h>
#include <pragma/asl_lib.h>

#include "Camedia.h"

struct Library *JpegBase, *CyberGfxBase;

void info_f(enum Camera_Speed cs);
void preview_f(enum Camera_Speed cs);
void retr_f(int user_min, int user_max, char *name, long speed, Camera_Speed cs);

void main(int argc, char *argv[]) {
	enum Camera_Speed cs;
	long speed=0;
	int info, from ,retr, to, i, unit, preview;
	LONG array[9];
	struct RDArgs *rdargs;
	char devicename[256], picture_name[256];

	for (i=0; i<10; i++) array[i]=0;
	rdargs=ReadArgs("DEVICE/K,UNIT/N,SPEED/N,INFO/S,PREVIEW/S,RETR/S,FROM/N,TO/N,NAME/K", array, NULL);
	if (rdargs==NULL) {
		printf("An error occured in ReadArgs(DEVICE/K,UNIT/N,SPEED/N,INFO/S,PREVIEW/S,RETR/S,FROM/N,TO/N,NAME/K array, NULL);\n");
		exit(0);
	}
	
	/* DEVICE/K */
	if (array[0]==0) strcpy(devicename, "serial.device");
	else strcpy(devicename, (char *) array[0]);
	
	/* UNIT/N */
	if (array[1]!=0) unit=*((long *) array[1]);
	else unit=0;
	
	/* SPEED/N */
	if (array[2]!=0) speed=*((long *) array[2]);
	else speed=19200;
	
	switch (speed) {
		case 9600:
			cs=CAMERA_SPEED_9600;
			break;
		case 19200:
			cs=CAMERA_SPEED_19200;
			break;
		case 38400:
			cs=CAMERA_SPEED_38400;
			break;			
		case 57600:
			cs=CAMERA_SPEED_57600;
			break;
		case 115200:
			cs=CAMERA_SPEED_115200;
			break;
		default:
			cs=CAMERA_SPEED_Unknown;
			printf("Unknown speed, must be either 9600, 19200, 38400, 57600 or 115200\n");
			exit(0);
	}
	
	/* INFO/S */ 
	info=(int) array[3];
	
	/* PREVIEW/S */
	preview=(int) array[4];
	
	/* RETR/S */
	retr=(int) array[5];
	
	/* FROM/N */
	if (array[6]!=0) from=(int) *((long *) array[6]);
	else from=0;
	
	/* TO/N */
	if (array[7]!=0) to=(int) *((long *) array[7]);
	else to=0;
	
	/* NAME/K */
	if (array[8]==0) strcpy(picture_name, "picture");
	else strcpy(picture_name, (char *) array[8]);	
	
	FreeArgs(rdargs);
	
	// printf("DEVICE/K = %s\nUNIT/N = %d\nSPEED/N = %d\nINFO/S = %d\nPREVIEW/S = %d\nRETR/S = %d\nFROM/N = %d\nTO/N = %d\nNAME/K = %s\n", devicename, unit, speed, info, preview, retr, from, to, picture_name);
	
	if ((info!=0) && (retr!=0) && (preview!=0)) {
		printf("Either INFO, PREVIEW or RETR but not more then one of them.\n");
		exit(0);
	}
	if ((info==0) && (retr==0) && (preview==0)) {
		printf("Either INFO, PREVIEW or RETR must be used.\n");
		exit(0);
	}
	try {
		bootstrap(devicename, unit);
	}
	catch (void *str) {
		printf("There was an error in bootstrap: %s\n", str);
		exit(0);
	}
	try {
		if (retr) {
			
			// The commented code would take a picture (into cameras memory)
			// However, there is a timeout, maybe cause of a slightly different protocol...
			//init(cs);
			//TakeAction(AC_Take_Picture, 0);
			if (from==0) from=1;
			if (to==0) to=255;
			retr_f(from, to, picture_name, speed, cs);
		}
		else if (preview) {
			preview_f(cs);
		} else {
			info_f(cs);
		}
		printf("Well, all done\n");
	} 
	catch (void *str) {
		printf("There was an error: %s\n", str);
	}
	clean(100);
}


void info_f(enum Camera_Speed cs) {
	Resolution *re=NULL;
	time_t *c=NULL, *TimeDate=NULL;
	long *ss=NULL;
	Aperture *a=NULL;
	Color_Mode *cm=NULL;
	Flash_Mode *fm=NULL;
	int *noft=NULL, *nofl=NULL, *bc=NULL, *aoht=NULL, *asoft=NULL, *lb=NULL, *lat=NULL;
	int *AEL_WBL=NULL, *Fisheye=NULL, *Wide=NULL, *Digital_Zoom=NULL, *BAW=NULL;
	int *LCD_Brightness=NULL, *Frames_Taken=NULL,*ProtectionState=NULL, *psof=NULL;
	Camera_Speed *ccs=NULL;
	Bright_Contrast *brc=NULL;
	White_Balance *wb=NULL;
	Date_Format *df=NULL;
	Exp_Meter *em=NULL;
	long *aml=NULL, *locf=NULL, *loct=NULL, *AudioDataLength=NULL;
	Lens_Mode *lm=NULL;
	int i, max;
	char buf[1024];

	try {
		init(cs);
	
		Get_Resolution(re);
		printf("Resolution=%s\n", dump(*re));

		Get_Clock(c);
		printf("Clock=%s\n", asctime(localtime(c)));
		
		Get_Shutter_Speed(ss);
		printf("Shutter_Speed=%d\n", *ss);
		
		/* Get_Aperture(a);
		 printf("Aperture=%s\n", dump(*a));
		 */
		 
		Get_Color_Mode(cm);
		printf("Color_Mode=%s\n", dump(*cm));
		
		Get_Flash_Mode(fm);
		printf("Flash_Mode=%s\n", dump(*fm));

		Get_Num_of_Frames_Taken(noft);
		printf("Number of Frames Taken=%d\n", *noft);
		max=*noft;
		
		Get_Num_of_Frames_Left(nofl);
		printf("Number of Frames Left=%d\n", *nofl);
		
		Get_Battery_Capacity(bc);
		printf("Battery Capacity=%d\n", *bc);
		
		/* Get_Comm_Speed(ccs);
		 printf("Comm Speed=%s\n", dump(*ccs));
		 */
		
		/* Get_Bright_And_Contrast(brc);
		 printf("Bright and Contrast=%s\n", dump(*brc));
		 */

		Get_White_Balance(wb);
		printf("White Balance=%s\n", dump(*wb));
	
		Get_Autoshut_on_Host_Timer(aoht);
		printf("Autoshut on Host Timer=%d\n", *aoht);
		
		Get_Autoshut_on_Field_Timer(asoft);
		printf("Autoshut on Field Timer=%d\n", *asoft);
		
		Get_Available_Memory_Left(aml);
		printf("Available Memory Left=%d [Bytes]\n", *aml);
		
		Get_Lens_Mode(lm);
		printf("Lens Mode=%s\n", dump(*lm));
		
		Get_LCD_Brightness(lb);
		printf("LCD Brightness=%d\n", *lb);
		
		Get_LCD_Autoshut_Timer(lat);
		printf("LCD Autoshut Timer=%d\n", *lat);
		
		Get_Date_Format(df);
		printf("Date Format=%s\n", dump(*df));
	
		Get_Exp_Meter(em);
		printf("Exp Meter=%s\n", dump(*em));
		
		/* Get_Optical_Mode(AEL_WBL, Fisheye, Wide, Digital_Zoom, BAW);
		 printf("AEL_WB=%dL, Fisheye=%d, Wide=%d, Digital_Zoom=%d, BAW=%d\n", *AEL_WBL, *Fisheye, *Wide, *Digital_Zoom, *BAW);
		 */

		Get_Camera_ID(buf, NULL);
		printf("Camera ID=%s\n", (char *) buf);
		
		Get_Serial_Number(buf, NULL);
		printf("Serial Number=%s\n", buf);
		
		Get_Version(buf, NULL);
		printf("Version=%s\n", buf);
		
		Get_Model((char *) buf, NULL);
		printf("Model=%s\n", buf);

		/* Get_Camera_Summery_Data(re, LCD_Brightness, Frames_Taken);
		 printf("Resolution=%s, LCD Brightness=%d, Frames Taken=%d\n", dump(*re), *LCD_Brightness, *Frames_Taken);
		 */
		 
		Get_Manufacturer((char *) buf, NULL);
		printf("Manufacturer=%s\n", buf);

		if (max>0) {
			i=1;
			printf("There are %d frames taken\n", max);
			while (i<=max && i<255) {
				printf("Frame[%d]:\n", i);

				Get_Length_of_Frame(i, locf);
				printf("   Length=%d\n", *locf);
			
				Get_Length_of_Thumbnail(i, loct);
				printf("   Length of Thumbnail=%d\n", *loct);

				Get_Protect_State_of_Frame(i, psof);
				printf("   Protection=%d\n", *psof);
				
				Get_Picture_Summery_Data(i, NULL, NULL, NULL, TimeDate);
				printf("   time stamp=%s\n", asctime(localtime(TimeDate)));

				i++;
			}
		} else printf("There are no pictures in the camera\n");
	}
	catch (void *str) {
		printf("%s\n", str);
	} 
}


void retr_f(int user_min, int user_max, char *picture_name, long speed, enum Camera_Speed cs) {
	time_t t;
	time_t time1, time2;
	char name[255], comment[80];
	int i, max, passedtime, NoFT;
	long size;
	struct tm *tm_s;
	
	if ((user_min<1) || (user_max<1) || (user_min>user_max)) {
		printf("Can't accept such ranges\n");
		return;
	}

	try {
		init(cs);

		Get_Num_of_Frames_Taken(&NoFT);
	
		max=(int) NoFT;
		if (max==0) {
			printf("No pictures are stored in the camera.\n");
			return;
		}
		if (max<user_min) {
			printf("There are %d pictures in the camera, not one more.\n");
			return;
		}
		if (max<user_max) user_max=max;
		
	 	printf("Will get pictures %d to %d\n", user_min, user_max);
		for(i=user_min; i<=user_max; i++) {
			sprintf(name, "%s_%d.jpeg", picture_name, i);		
			Get_Length_of_Frame(i, &size);

			printf("Get picture %d, size %d bytes, transfer time: about %d s\n", i, size, (int) 3+size/(speed/10));

			Get_Picture_Summery_Data(i, NULL, NULL, NULL, &t);
		
		
			tm_s = localtime(&t);
			if (((tm_s->tm_year)<95) && ((tm_s->tm_year)>105)) strcpy(comment, "no date set");
			else strftime(comment, 80, "%d-%b-%Y %H:%M:%S", tm_s );
		
			time(&time1);
			save_Frame(i, name, comment); 

			time(&time2);
			passedtime=(int) time2-time1;

			printf("%s, size %d, transfered in %d s, %5.0f cps\n", name, size, passedtime, ((float) size) / ((float) passedtime) );

		} // end for
	}
	catch (void *str) {
		printf("%s\n", str);
	}
}

void preview_f(enum Camera_Speed cs) {
	struct JPEGDecHandle *jph;
	struct Screen *scr;
	struct Window *win;
	struct Message *msg;
	UBYTE *buffer=NULL;
	UBYTE *frame_buffer=NULL;
	ULONG DisplayID;
	UWORD DisplayDepth;
	struct ScreenModeRequester *smr;

	int err;
	ULONG x, y, count, bpp;
	UBYTE colorspace;
	long *size=0;
	BOOL r;
		

	/* Open Libraries */
	
	JpegBase = OpenLibrary( "jpeg.library", NULL );
	if (JpegBase==NULL) {
		printf("Couldn't open jpeg.library\n");
		return;
	}
	
	CyberGfxBase = OpenLibrary( "cybergraphics.library", NULL );
	if (JpegBase==NULL) {
		printf("Couldn't open cybergraphics.library\n");
		goto end5;
	}
	
	/* Open ASL and ask user about screen */
	
	smr = (struct ScreenModeRequester* ) AllocAslRequestTags(ASL_ScreenModeRequest,
		ASLSM_MinDepth, (ULONG) 15,
		TAG_DONE);
		
	if (smr==NULL) {
		printf("Couldn't do an AllocAslRequestTags()\n");
		goto end4;
	}
	
	r = AslRequest(smr, TAG_DONE);
	if (!r) {
		FreeAslRequest(smr);
		printf("User did nothing?\n");
		goto end4;
	}
	
	DisplayID=smr->sm_DisplayID;
	DisplayDepth=smr->sm_DisplayDepth;
	FreeAslRequest(smr);
	
	/* Open Screen and Window */
	
	scr = OpenScreenTags( NULL,
								SA_Title, "Camedia_Snapshot",
								//SA_DisplayID, 0x40120051,
								SA_DisplayID, DisplayID,
								//SA_Depth, GetCyberIDAttr( CYBRIDATTR_DEPTH, 0x40120051),
								SA_Depth, DisplayDepth,
								TAG_DONE );
	if (scr==NULL) {
		printf("Couln't open screen\n");
		goto end4;
	}
	
	win = OpenWindowTags( NULL,
								WA_Title, "Preview",								
								WA_Flags, WFLG_ACTIVATE | WFLG_SIMPLE_REFRESH |
								WFLG_SIZEGADGET | WFLG_RMBTRAP | WFLG_DRAGBAR |
								WFLG_DEPTHGADGET | WFLG_CLOSEGADGET,
								WA_IDCMP, IDCMP_CLOSEWINDOW,
								WA_Left, 0,
								WA_Top, 0,
								WA_Width, 500,
								WA_Height, 380,
								WA_MinWidth, 100,
								WA_MinHeight, 70,
								WA_MaxWidth, scr->Width,
								WA_MaxHeight, scr->Height,
								WA_AutoAdjust, 1,
								WA_CustomScreen, scr,
								TAG_DONE );

	if ( win == NULL ) {
		printf("Couldn't open window\n");
		goto end3;
	}
	
	buffer=(UBYTE *) AllocMem(256000+6000, MEMF_CLEAR);
	if (buffer==NULL) {
		printf("No memory\n");
		goto end2;
	}
	frame_buffer=buffer+256000;
	
	/* retrieve previews */
	
	try {
		init(cs);
	}
	catch(void *str) {
		printf("Error in init() %s\n", str);
		goto end2;
	}
	
	for (;;) {
		msg=GetMsg( win->UserPort );
		if (msg!=NULL) {
			ReplyMsg(msg);
			while ( ( msg = GetMsg( win->UserPort ) ) != NULL ) ReplyMsg( msg );
			goto end1;
		}
		
		try {
			Get_Preview((UBYTE *) frame_buffer, size);
		}
		catch (void *str) {
			printf("Error in Get_Preview() %s\n", str);
			goto end1;
		}

		/* Decode picture */
		
		err = AllocJPEGDecompress( &jph,
				JPG_SrcMemStream, frame_buffer,
				JPG_SrcMemStreamSize, *size,
				TAG_DONE );

		if (err!=0) {
			printf("error in AllocJPEGDecompress\n");
			goto end1;
		}
		err = GetJPEGInfo( jph,
								JPG_Width, &x, JPG_Height, &y,
								JPG_ColourSpace, &colorspace, 
								JPG_BytesPerPixel, &bpp,
								JPG_ScaleNum, 1, JPG_ScaleDenom, 1,
								TAG_DONE );
		
		if (err!=0) {
			printf("error in GetJPEGInfo\n");
			FreeJPEGDecompress(jph);
			goto end1;
		}

		err = DecompressJPEG( jph,
									JPG_DestRGBBuffer, buffer,
									JPG_ScaleNum, 1, JPG_ScaleDenom, 1,
									TAG_DONE );
		if (err!=0) {
			printf("error in DecompressJPEG(...)\n");
			FreeJPEGDecompress(jph);
			goto end1;
		}
	
		/* Display picture */
		count= (ULONG) ScalePixelArray(buffer, x, y, (bpp*x), win->RPort, win->BorderLeft, win->BorderTop, win->GZZWidth, win->GZZHeight, RECTFMT_RGB);

		FreeJPEGDecompress(jph);
	}
	
	/* Close Windows and Screen*/

end1:
	FreeMem(buffer, 256000+6000);
end2:
	CloseWindow(win);
end3:
	CloseScreen(scr);
end4:
	/* Close Libraries */
	CloseLibrary(CyberGfxBase);
end5:
	CloseLibrary(JpegBase);
}