/* Thomas Reinhardt */
#include <time.h>
#include <string.h>
#include <exec/interrupts.h>
#include <exec/libraries.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/ports.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/diskfont.h>
#include <stdio.h>
#include <stdlib.h>

#include "DeInterlace.h"

/*
#define abs(x) ((x)<0?-(x):(x))
*/

LONG FileNamePointer,SaveNamePointer;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *DosBase;
struct NewScreen MyNewScreen;
struct Screen *MyScreen;
struct NewWindow VorWin;
struct Window *MyWindow;
struct RastPort *MyRastPort;
struct BitMapHeader BMHD;
struct ColorRegister Colors[256];
struct CommodoreAmiga CAMG;
int Mask[9]={1,2,4,8,16,32,64,128,256};
LONG Len,x,y,b;
ULONG ChunkLen;
BOOL BMHDFlag,CMAPFlag,CAMGFlag,BODYFlag,FoundChunk,ShowFlag=FALSE;
UBYTE Buffer[1000];
UBYTE ByteCount;
UBYTE BytesPerRow;
char *WohinDamit;
BOOL success,AllDone;
int i;
char inread[5];

int main(int argc,char **argv){
	if(argc==3){
		DosBase=(struct Library *)OpenLibrary("dos.library",0L);
		GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0L);
		IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L);
		if((DosBase==0)||(GfxBase==0)||(IntuitionBase==0)) DisplayErrorMessage("Error opening libraries");
	  BMHDFlag=CMAPFlag=CAMGFlag=BODYFlag=FALSE;
		FileNamePointer=Open(argv[1],MODE_OLDFILE);
		if(!FileNamePointer){
			DisplayErrorMessage("Fehler : Datei nicht gefunden!");
			}
		else{
			AllDone=FALSE;
			success=Einlesen(&Buffer[0],12,TRUE);
			if(strncmp(&Buffer[0],"FORM",4)==0){
				printf("Kennung >FORM< found ...\n");
				if(strncmp(&Buffer[8],"ILBM",4)==0){
					printf("Kennung >ILBM< found ...\n");
					Loop:
					FoundChunk=FALSE;
					Einlesen(&Buffer[0],8,FALSE);
					if(Len<=0){
						if((BMHDFlag==TRUE)&&(BODYFlag==TRUE)&&(CMAPFlag==TRUE)){
							if(CAMGFlag==FALSE) printf("Kein CommodoreAmiga!\n");
							DeInterlace();
							SaveFile(argv[2]);
							DisplayErrorMessage("Operation ended...");
							}
						else{
							if(BMHDFlag!=TRUE) DisplayErrorMessage("Kein BMHD");
							if(BODYFlag!=TRUE) DisplayErrorMessage("Kein BODY");
							if(CMAPFlag!=TRUE) DisplayErrorMessage("Kein CMAP");
							}
						}
					ChunkLen=Buffer[4]*16777216+Buffer[5]*65536+Buffer[6]*256+Buffer[7];
					if(strncmp(Buffer,"BMHD",4)==0){
						printf("Kennung >BMHD< found ...\n");
						if(BMHDFlag==TRUE) DisplayErrorMessage("Found second BMHD !");
						Einlesen(&BMHD,ChunkLen,TRUE);
						MyNewScreen.LeftEdge=0;
						MyNewScreen.TopEdge=0;
						MyNewScreen.Width=BMHD.Width;
						MyNewScreen.Height=BMHD.Height;
						MyNewScreen.Depth=BMHD.nPlanes;
						MyNewScreen.DetailPen=1;
						MyNewScreen.BlockPen=0;
						if(CAMGFlag==TRUE) MyNewScreen.ViewModes=CAMG.ViewModes;
						else{
							MyNewScreen.ViewModes=0;
							if(MyNewScreen.Width>320) MyNewScreen.ViewModes|=HIRES;
							if(MyNewScreen.Height>256) MyNewScreen.ViewModes|=LACE;
							}
						MyNewScreen.Type=CUSTOMSCREEN;
						MyNewScreen.Font=NULL;
						MyNewScreen.DefaultTitle=(char *)argv[1];
						MyNewScreen.Gadgets=NULL;
						MyNewScreen.CustomBitMap=NULL;
						MyScreen=OpenScreen(&MyNewScreen);
						if(MyScreen==0) DisplayErrorMessage("No Screen ???");
						BytesPerRow=BMHD.w/8;
						if(((float)BytesPerRow)!=(((float)BMHD.w)/8)){
							BytesPerRow++;
							}
						ShowTitle(MyScreen,FALSE);
						VorWin.LeftEdge=0;
						VorWin.TopEdge=0;
						VorWin.Width=BMHD.w;
						VorWin.Height=BMHD.h;
						VorWin.DetailPen=0;
						VorWin.BlockPen=0;
						VorWin.Title=NULL;
						VorWin.Flags=ACTIVATE|BORDERLESS|BACKDROP|SMART_REFRESH;
						VorWin.IDCMPFlags=MOUSEMOVE|MOUSEBUTTONS;
						VorWin.FirstGadget=NULL;
						VorWin.CheckMark=NULL;
						VorWin.Type=CUSTOMSCREEN;
						VorWin.Screen=MyScreen;
						VorWin.BitMap=NULL;
						VorWin.MinWidth=BMHD.w;
						VorWin.MinHeight=BMHD.h;
						VorWin.MaxWidth=BMHD.w;
						VorWin.MaxHeight=BMHD.h;
						if(NULL!=(MyWindow=OpenWindow(&VorWin))){
							ShowTitle(MyScreen,FALSE);
							MyRastPort=MyWindow->RPort;
							SetDrMd(MyRastPort,JAM2);
							}
						BMHDFlag=TRUE;
						FoundChunk=TRUE;
						}
					if(strncmp(Buffer,"CMAP",4)==0){
						printf("Kennung >CMAP< found ...\n");
						if(CMAPFlag==TRUE) DisplayErrorMessage("found second CMAP!");
						if(BMHDFlag==FALSE) DisplayErrorMessage("need at first BMHD");
						Einlesen(Colors,ChunkLen,TRUE);
						for(i=0;i<Mask[BMHD.nPlanes];i++){
							SetRGB4(&MyScreen->ViewPort,i,Colors[i].rot>>4,Colors[i].gruen>>4,Colors[i].blau>>4);
							}
						CMAPFlag=TRUE;
						FoundChunk=TRUE;
						}
					if(strncmp(Buffer,"BODY",4)==0){
						printf("Kennung >BODY< found ...\n");
						if(BODYFlag==TRUE) DisplayErrorMessage("found second BODY!");
						if(BMHDFlag==FALSE) DisplayErrorMessage("need at first BMHD");
						for(y=0;y<BMHD.h;y++){
							for(b=0;b<BMHD.nPlanes;b++){
								ByteCount=0;
								WohinDamit=(char *)MyScreen->RastPort.BitMap->Planes[b]+y*(BMHD.Width/8);
								if(BMHD.compression==0)
									Einlesen(WohinDamit,BytesPerRow,TRUE);
								if(BMHD.compression==1){
									while(ByteCount<BytesPerRow){
										Einlesen(&Buffer[0],1,TRUE);
										if(Buffer[0]<128){
											Einlesen(WohinDamit+ByteCount,Buffer[0]+1,TRUE);
											ByteCount+=Buffer[0]+1;
											}
										if(Buffer[0]>128){
											Einlesen(&Buffer[1],1,TRUE);	
											for(i=ByteCount;i<(ByteCount+257-Buffer[0]);i++) *(WohinDamit+i)=Buffer[1];
											ByteCount+=257-Buffer[0];
											}
										}
									}
								}
							}
						BODYFlag=TRUE;
						FoundChunk=TRUE;
						AllDone=TRUE;
						}
					if(strncmp(Buffer,"CAMG",4)==0){
						printf("Kennung >CAMG< found ...\n");
						if(CAMGFlag==TRUE) DisplayErrorMessage("found second CAMG");
						if(BMHDFlag==FALSE) DisplayErrorMessage("need first BMHD");
						Einlesen(&CAMG,ChunkLen,TRUE);
						MyScreen->ViewPort.Modes=CAMG.ViewModes;
						RemakeDisplay();
						CAMGFlag=TRUE;
						FoundChunk=TRUE;
						}
					if((FoundChunk==FALSE)&&(AllDone==FALSE)){
						Einlesen(&Buffer,ChunkLen,FALSE);
						if((ChunkLen&1)==1) Einlesen(Buffer,1,FALSE);
						}
					goto Loop;
					}
				else{
					DisplayErrorMessage("Falsches InputFile!");
					}
				}
			else{
				DisplayErrorMessage("Falsches InputFile!");
				}
			}
		DisplayErrorMessage("all done ... aborting!");
		return 0;
		}
	else{
		DisplayErrorMessage("USAGE : DeInterlace <infile> <outfile>");
		}
	}

BOOL Einlesen(char *Buffer,WORD Anz,BOOL Flag){
	Len=Read(FileNamePointer,Buffer,Anz);
	if((Flag==TRUE)&&(Len<0)) DisplayErrorMessage("File is corrupt!");
	return TRUE;
	}

BOOL SaveFile(char *OpenFileName){
	ULONG zwsp[1];
  int yi,b;

	SaveNamePointer=Open(OpenFileName,MODE_NEWFILE);
	if(!SaveNamePointer){
		DisplayErrorMessage("Error : Save-Gfx(IFF)-Datei!");
		}
	else{
		Write(SaveNamePointer,"FORM",4);
		zwsp[0]=4;	/* ILBM */
		zwsp[0]+=sizeof(BMHD)+8; /* BMHD */
		zwsp[0]+=Mask[BMHD.nPlanes]*3*3; /* CMAP */
		zwsp[0]+=8; /* CMAP-Header */
		zwsp[0]+=12; /* CAMG */
		zwsp[0]+=BMHD.Width*BMHD.Height*BMHD.nPlanes/8+8; /* BODY */
		Write(SaveNamePointer,&zwsp[0],4);
		Write(SaveNamePointer,"ILBM",4);
		Write(SaveNamePointer,"CMAP",4);
		zwsp[0]=Mask[BMHD.nPlanes]*3*3;
		Write(SaveNamePointer,&zwsp[0],4);
		Write(SaveNamePointer,&Colors[0],Mask[BMHD.nPlanes]*3*3);
		Write(SaveNamePointer,"CAMG",4);
		zwsp[0]=4;
		Write(SaveNamePointer,&zwsp[0],4);
		zwsp[0]=4;
		Write(SaveNamePointer,&CAMG,4);
		Write(SaveNamePointer,"BMHD",4);
		BMHD.compression=0;
		BMHD.masking=0;
		zwsp[0]=sizeof(BMHD);
		Write(SaveNamePointer,&zwsp[0],4);
		Write(SaveNamePointer,&BMHD,sizeof(BMHD));
		Write(SaveNamePointer,"BODY",4);
		zwsp[0]=BMHD.Width*BMHD.Height*BMHD.nPlanes/8;
		Write(SaveNamePointer,&zwsp[0],4);
		for(yi=0;yi<BMHD.Height;yi++){
			for(b=0;b<BMHD.nPlanes;b++){
				Write(SaveNamePointer,MyScreen->RastPort.BitMap->Planes[b]+yi*(BMHD.Width/8),BMHD.Width/8);
				}
			}
		}
	return TRUE;
	}

void DisplayErrorMessage(char *Meldung){
	printf("%s\n",Meldung);
	if(MyWindow) CloseWindow(MyWindow);
	if(FileNamePointer) Close(FileNamePointer);
	if(SaveNamePointer) Close(SaveNamePointer);
	if(MyScreen) CloseScreen(MyScreen);
	if(DosBase) CloseLibrary(DosBase);
	if(GfxBase) CloseLibrary((struct Library *)GfxBase);
	if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	exit(TRUE);
	}

void DeInterlace(){
	int xx,yy;
	UBYTE Zwsp;
	LONG Red1,Blue1,Green1,Red2,Blue2,Green2,RedNeu,BlueNeu,GreenNeu;
	LONG dummy,dummyNr,dummyMax;
	for(yy=1;yy<BMHD.h;yy=yy+2){
		for(xx=0;xx<BMHD.w;xx++){
			Zwsp=ReadPixel(MyRastPort,xx,yy-1);
			Red1=Colors[Zwsp].rot;
			Blue1=Colors[Zwsp].blau;
			Green1=Colors[Zwsp].gruen;
			Zwsp=ReadPixel(MyRastPort,xx,yy+1);
			Red2=Colors[Zwsp].rot;
			Blue2=Colors[Zwsp].blau;
			Green2=Colors[Zwsp].gruen;
			RedNeu=(Red1+Red2)/2;
			GreenNeu=(Green1+Green2)/2;
			BlueNeu=(Blue1+Blue2)/2;
			dummyMax=256*3;
			dummy=0;
			dummyNr=-1;
			for(i=0;i<Mask[BMHD.nPlanes];i++){
				dummy=abs(RedNeu-Colors[i].rot);
				dummy=dummy+abs(BlueNeu-Colors[i].blau);
				dummy=dummy+abs(GreenNeu-Colors[i].gruen);
				if (dummy<dummyMax){
					dummyMax=dummy;
					dummyNr=i;
					}
				dummy=0;
				}
			SetAPen(MyRastPort,dummyNr);
			WritePixel(MyRastPort,xx,yy);
			}		
		}
	}
