#include "main.h" 
#include <pspgu.h> 


#include "utils.h"
#include "font.h"
#include "attractions.h"
#include "nmeap.h"
#include "time.h"
#include "danzeff.h"
#define printf  pspDebugScreenPrintf


//config variables:
int baud=38400;
int fakefeed=0;
int checksum=0;
double speedfix=1.151;
double altfix=1;
int timezone=0;
int rotatemap=0;

FILE *gpsout=NULL;
SceUID wifithid=0;

extern ATTRACTION * attractions[MAXNUM];
#define EVENT_SIO       0x01
SceCtrlData cpad;
Image * current, *attraction=NULL, *rotimage ;
int TILE_NUM;
int zoom ;
int zm;
int gpsOn=0;
int basezoom=2;
int mapsize;
char dirname[128];
int maxzoom;
int oldzoom;
int mapx ;
int mapy ;
int killwifi=0;
double mx,my;
double lat=0;
double lon=0;
double deltax=0;
double deltay=0;
double oldx=0;
double oldy=0;
double distance=0;
double bearing=0;
char zipfile[100];
char filetype[5];
char messages [15][64];
int messagecount=0;
int tlock=0;
char options[10][60];

char heading[8][3] = { " N", "NE", " E", "SE", " S", "SW", " W", "NW" };
int optcount=0;
int datatype=0;         // 0=dir; 1=zipfile

double lat1, lon1;
double ftx, fty;
int firsttime=1;
int idle=0;

int attr_flag=1;
char attr_ifile[128];
int attr_width=16;
int attr_height=8;
FILE * fp;
int gpsdone=0;
u64 currTick;
u64 deltatime;
u64 frameTick;
u64 frameDelta;



// TODO: should be moved to PSPSDK
int sceNetResolverCreate(int *rid, void *buf, SceSize buflen);
int sceNetResolverStartNtoA(int rid, const char* hostname, struct in_addr* addr, unsigned int timeout, int retry);
int sceNetResolverStartAtoN(int rid, const struct in_addr* addr, char *hostname, SceSize hostname_len, unsigned int timeout, int retry);
int sceNetResolverStop(int rid);
int sceNetInetInetAton(const char* host, struct in_addr* addr);

static char resolverBuffer[1024];
static int resolverId;

Coord getGPS (int x, int y); 
Coord getTileCoord(double lat, double lon, int zoomLevel ); 


static  double pixelsPerLonDegree[18];
static  double pixelsPerLonRadian[18];
static  double numTiles[18];
static  double bitmapOrigo[18];
static  double bc = 2 * PI;
static  double Wa = PI / 180;


#ifndef SDL_SOUND
PSP_MODULE_INFO("Google Map Viewer", 0x1000, 1, 1);
PSP_MAIN_THREAD_ATTR(0);
#else
static Mix_Chunk *chunk;
#endif

nmeap_gga_t g_gga;
static nmeap_context_t nmea;
static nmeap_gga_t     gga;
static nmeap_rmc_t     rmc;
static int             user_data; 
int status=0;

double        ggalatitude;
double        ggalongitude;
double        altitude;
unsigned long ltime;
int           satellites;
int           quality;
double        hdop;
double        geoid;
char          warn[4]="BAD";


double        latitude;
double        longitude;
double        speed;
double        course;
unsigned long date;
double        magvar;

static SceUID g_eventflag = -1;

void splashscreen() {
	Image * splashscreen=loadImage("system/splashscreen.png");
	blitAlphaImageToScreen(0,0,PSP_WIDTH,PSP_HEIGHT,splashscreen,0,0);
	flipScreen();
	sceKernelDelayThread(3000000);
	if (splashscreen!=NULL)
		freeImage(splashscreen);
}
void helpscreen() {
	Image * helpscreen=loadImage("system/psphelp.png");
	blitAlphaImageToScreen(0,0,PSP_WIDTH,PSP_HEIGHT,helpscreen,0,0);
	flipScreen();
	sceKernelDelayThread(3000000);
	if (helpscreen!=NULL)
		freeImage(helpscreen);
}

/*
int checkForMAC(int checksum) {
char sVal[7]; 
   int retVal; 
   int vals[7];
   memset(sVal, 0, 7); 
   retVal = sceWlanGetEtherAddr(sVal); 
   if (retVal == 0)  
   {      
	for (int i=0; i<6; i++) {
		vals[i]=sVal[i];
		if (vals[i]<0)
			vals[i]+=256;
	}
      fprintf(stdout,"MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]); 
	if (checksum==(vals[0]+vals[1]-vals[2]*vals[3]-vals[4]-vals[5]))
		return 1;
	else
		return 0;

   } 
   fprintf(stdout,"Error getting Wlan Ethernet Address (0x%08X)\n", retVal); 
	return 0;

}
*/


void readConfig() {
	FILE * fp=fopen("system/config.txt", "r");
	if (fp==NULL)
		return;
	char line[128];
	while(fgets(line,128,fp) != NULL) {
		if (line[0]=='#')
			continue;
		if (strncmp(line,"BAUD=",5)==0) {
			baud=atoi(&line[5]);
		}
		if (strncmp(line,"FAKEFEED=",9)==0) {
			fakefeed=atoi(&line[9]);
		}
		if (strncmp(line,"CHECKSUM=",9)==0) {
			checksum=atoi(&line[9]);
		}
		if (strncmp(line,"SPEEDFIX=",9)==0) {
			speedfix=atof(&line[9]);
		}
		if (strncmp(line,"ALTFIX=",7)==0) {
			altfix=atof(&line[7]);
		}
		if (strncmp(line,"TIMEZONE=",9)==0) {
			timezone=atoi(&line[9]);
		}
	}

	fclose(fp);
}

void myprint(char * str) {
	memset(messages[messagecount],0,64);
	strcpy(messages[messagecount++], str); 
	if (messagecount>14) {
		for (int i=0; i<15; i++)
			strcpy(messages[i], messages[i+1]);
		messagecount--;
	}
}

void printMessage(const char *fmt, ...) {
	fprintf(stdout,fmt);
//	memset(messages[messagecount],0,64);
//	sprintf(messages[messagecount++],fmt);
//	if (messagecount>9)
//		messagecount=0;
}


int calc_mapsize(int sz) {
	int i=sz;
	int total=0;
	while ( i>1) {
		total+=i*i;
		i/=2;
	}
	return total;
}

int wget(SceSize argc, void* argv)
{
	killwifi=0;
	int tilenumber= calc_mapsize(mapsize);
	char mybuf[64];
	sprintf(mybuf,"PREPAIRING TO GET %d TILES", tilenumber);
	myprint(mybuf);
    	myprint("CONNECTING....");
 FILE * fp;
   // init wlan
   int err = pspSdkInetInit();
   if (err != 0) {
      printf("InetInit failed: %i", err);
      myprint("INET INIT FAILED");
      return -1;
   }

   // print available connections
   printf("available connections:\n");
   int i;
   for (i = 1; i < 100; i++) // skip the 0th connection
   {
      if (sceUtilityCheckNetParam(i) != 0) break;  // no more
      char name[64];
      sceUtilityGetNetParam(i, 0, (netData*) name);
      printf("%i: %s\n", i, name);
	if (killwifi)
		return -1;
   }

   // use connection 1
   printf("using first connection\n");
   err = sceNetApctlConnect(1);
   if (err != 0) {
      sprintf(mybuf,"sceNetApctlConnect failed: %i", err);
	myprint(mybuf);
      return -1;
   }

   // WLAN is initialized when PSP IP address is available
   printf("init WLAN and getting IP address");
	myprint("INIT WLAN AND GETTING IP ADDRESS...");
   char pspIPAddr[32];
   while (1) {
      if (sceNetApctlGetInfo(8, pspIPAddr) == 0) break;
      myprint(".");
      sceKernelDelayThread(1000 * 1000);  // wait a second
   }
   sprintf(mybuf,"\nPSP IP address: %s\n", pspIPAddr);
  myprint(mybuf);

   // start DNS resolver
   err = sceNetResolverCreate(&resolverId, resolverBuffer, sizeof(resolverBuffer)); 
 if (err != 0) {
      printf("sceNetResolverCreate failed: %i", err);
      myprint("NET RESOLVER FAILED..");
      return -1;
   }

   // resolve host
   myprint("RESOLVING HOST...\n");
   const char *host = "mt1.google.com";
   int port = 80;
   struct sockaddr_in addrTo;
   addrTo.sin_family = AF_INET;
   addrTo.sin_port = htons(port);
   //err = sceNetInetInetAton(host, &addrTo.sin_addr);
   //err = sceNetInetInetAton(host, &addrTo.sin_addr);
   if (err == 0) {
      err = sceNetResolverStartNtoA(resolverId, host, &addrTo.sin_addr, 2, 3);
      if (err != 0) {
         myprint("RESOLUTION FAILED...");
         return -1;
      }
   }
    	Coord tileco=getTileCoord(latitude,longitude,basezoom);
	int ms_original = mapsize;
   	mkdir(dirname, S_IREAD| S_IWRITE);

	int tilex=( (int)(tileco.lon/mapsize)) *mapsize;
	int tiley=( (int)(tileco.lat/mapsize)) *mapsize;

	char curdir[200];
	sprintf(curdir,"%s/coords.txt",dirname);
	FILE * cf=fopen(curdir,"w");
	if (cf!=NULL) {
		fprintf(cf,"%d %d %d\n", tilex, tiley, basezoom);
		fclose(cf);
	}
        int counter=0;;

	while(mapsize > 1) {	
		if (killwifi)
			return -1;
		int mapzoom= ms_original/mapsize;
		sprintf(curdir,"%s/%dx",dirname,mapzoom);
		mkdir (curdir, S_IREAD| S_IWRITE);
		int yc=0;
		while (yc < mapsize) {
			sprintf(curdir,"%s/%dx/%03d",dirname,mapzoom,yc);	
			mkdir (curdir, S_IREAD| S_IWRITE);
			int xc=0;
			
			while (xc < mapsize) {	

   // create socket (in blocking mode)
   //myprint("CREATING SOCKET...\n");
   int sock = socket(AF_INET, SOCK_STREAM, 0);
   if (sock < 0) {
      myprint("SOCKET FAILED");
      return -1;
   }

   // connect (this may block some time)
   //myprint("CONNECTING...\n");
   err = connect(sock, (struct sockaddr*) &addrTo, sizeof(addrTo));
   if (err != 0) {
      myprint("CONNECT FAILED");
      return -1;
   }
 // send HTTP request
   sprintf(mybuf,"LOADING %d OUT OF %d", counter++,tilenumber);
	myprint(mybuf);
   char request[512];
   sprintf(request,"GET /mt?v=w2.21&x=%d&y=%d&zoom=%d HTTP/1.0\r\nhost: mt1.google.com\r\n\r\n",tilex+xc,tiley+yc,basezoom);
   err = send(sock, request, strlen(request), 0);
   if (err < 0) {
      myprint("send failed");
      return -1;
   }

   // read all
   int capacity = 256;
   int len = 0;
   u8* buffer = (u8*) malloc(capacity);
   while (1) {
      printf(".");

      // read data
      int count = recv(sock, (u8*) &buffer[len], capacity - len, 0);

      // in blocking mode it has to return something, otherwise it is closed
      // (which is the default for HTTP/1.0 connections)
      if (count == 0) break;

      if (count < 0) {
         myprint("READ ERROR");
         break;
      }

      // adjust buffer, if needed
      len += count;
      if (len + 256 > capacity) {
         capacity *= 2;
         buffer = (u8*) realloc(buffer, capacity);
         if (!buffer) break;
      }
   }
  // now "len" bytes data are in buffer, add a 0 at end for printing and search for header end
   if (buffer) {
      buffer[len] = 0;
        int j;

        for (j=4; j<len ; j++) {
                if (buffer[j]=='\n' && buffer[j-1]=='\r' &&buffer[j-2]=='\n' &&buffer[j-3]=='\r')
                        break;

        }
        j++;
	sprintf(curdir,"%s/%dx/%03d/%dx%03d%03d.png",dirname,mapzoom,yc,mapzoom,yc,xc);	
         fp=fopen(curdir, "w");
        if (fp!=NULL) {
                for (i=j; i<len ; i++)
                        fputc(buffer[i],fp);
                fclose(fp);
        }
         //printf("\n\n%s", page);
         //myprint("DONE!");
      free(buffer);
   }

        close(sock);
	xc++;
}
	yc++;
}
	tilex/=2;
	tiley/=2;
	basezoom+=1;
	mapsize/=2;
}
   ///////////// PSP specific part ///////////////

   // term, TODO: this doesn't work
   sceNetApctlDisconnect();
   pspSdkInetTerm();
  myprint("DOWNLOAD COMPLETED!!!");
  sceKernelExitDeleteThread(0);
}






/*

static int intr_handler(void *arg)
{
        u32 stat;

        stat = _lw(0xBE500040);
        _sw(stat, 0xBE500044);

        sceKernelDisableIntr(PSP_HPREMOTE_INT);

        sceKernelSetEventFlag(g_eventflag, EVENT_SIO);

        return -1;
}




int sioReadChar(void)
{
        int ch;
        u32 result;
         SceUInt timeout;

        timeout = 100;

        ch = sioReadChar();
        if(ch == -1)
        {
                sceKernelEnableIntr(PSP_HPREMOTE_INT);
                sceKernelWaitEventFlag(g_eventflag, EVENT_SIO, 0x21, &result, &timeout);

                ch = pspDebugSioGetchar();
        }

        return ch;
}
*/

void display_message(char* line1, char* line2, char* line3, int timeout) {

	int len=30, counter=0;

	if (len<=strlen(line1)) len=strlen(line1)+4;
	if (len<=strlen(line2)) len=strlen(line2)+4;
	if (len<=strlen(line3)) len=strlen(line3)+4;

	char * messageline = (char*) malloc(len*sizeof(char));
	memset(messageline,' ', sizeof(char)*len-1);
	messageline[len-1]='\0';

	int x= PSP_WIDTH/2 - len*4;
	draw_string (messageline,FONT02,x,102);
	draw_string (messageline,FONT02,x,110);
	draw_string (messageline,FONT02,x,142);

	memset(messageline,' ', sizeof(char)*len-1);
	messageline[len-1]='\0';
	//strncpy(&messageline[1],"INFORMATION:",12);
	//draw_string (messageline,FONT02,x,102);

	memset(messageline,' ', sizeof(char)*len-1);
	messageline[len-1]='\0';
	strncpy(&messageline[(len -strlen(line1))/2],line1, strlen(line1));
	draw_string (messageline,FONT02,x,118);

	memset(messageline,' ', sizeof(char)*len-1);
	messageline[len-1]='\0';
	strncpy(&messageline[(len -strlen(line2))/2],line2, strlen(line2));
	draw_string (messageline,FONT02,x,126);

	memset(messageline,' ', sizeof(char)*len-1);
	messageline[len-1]='\0';
	strncpy(&messageline[(len -strlen(line3))/2],line3, strlen(line3));
	draw_string (messageline,FONT02,x,134);

	flipScreen(); 
        while (!(cpad.Buttons & PSP_CTRL_CROSS) && counter < timeout ) {
	 	sceCtrlPeekBufferPositive(&cpad, 1); 
                sceKernelDelayThread(1);
		counter++;
		sceKernelDelayThread(1000);
	}

	free (messageline);

}

static void print_gga(nmeap_gga_t *gga)
{
	    Coord xy_coord;
            ggalatitude=gga->latitude; 
            ggalongitude=gga->longitude;
            altitude=gga->altitude , 
            ltime=gga->time; 
            satellites=gga->satellites;
            quality=gga->quality; 
            hdop=gga->hdop;
            geoid=gga->geoid;  
	    gpsdone++;
	    //fprintf(stdout,"print_gga:: %10.6f::%10.6f\n", ggalatitude, ggalongitude);

	//	deltatime=currTick; 
	//	sceRtcGetCurrentTick(&currTick);
	//	deltatime=currTick-deltatime;
	  //  xy_coord=getXY(latitude, longitude, basezoom);
	   if (xy_coord.lon>0 && xy_coord.lon<TILE_SIZE*TILE_NUM && xy_coord.lat>0 && xy_coord.lat<TILE_SIZE*TILE_NUM) {
	  //  	mapx=xy_coord.lon;
	  //  	mapy=xy_coord.lat;
	//	deltax=mapx-oldx;
	//	deltay=mapy-oldy;
	//	oldx=mapx;
	//	oldy=mapy;
	}
	  // } else {
	   //     display_message(""," LOCATION OUT OF RANGE!! ","",100);
	//}
}


static void print_rmc(nmeap_rmc_t *rmc)
{
	    Coord xy_coord;
            ltime=rmc->time;
	    if (rmc->warn=='A')
            	strcpy(warn, "OK ");
	    else
            	strcpy(warn, "BAD");
            latitude=rmc->latitude;
            longitude=rmc->longitude;
            speed=rmc->speed;
            course=rmc->course;
            date=rmc->date;
            magvar=rmc->magvar;
	    //fprintf(stdout,"print_rmc:: %10.6f::%10.6f\n", latitude, longitude);
	   gpsdone++;
		deltatime=currTick; 
		sceRtcGetCurrentTick(&currTick);
		deltatime=currTick-deltatime;

	    xy_coord=getXY(latitude, longitude, basezoom);
	   if (xy_coord.lon>0 && xy_coord.lon<TILE_SIZE*TILE_NUM && xy_coord.lat>0 && xy_coord.lat<TILE_SIZE*TILE_NUM) {
	    	mapx=xy_coord.lon;
	    	mapy=xy_coord.lat;
		mx=mapx;
		my=mapy;
		deltax=mapx-oldx;
		deltay=mapy-oldy;
		oldx=mapx;
		oldy=mapy;
	   } else {
	       display_message(""," LOCATION OUT OF RANGE!! ","",300);
	}
}



/** called when a gprmc message is received and parsed */
static void gprmc_callout(nmeap_context_t *context,void *data,void *user_data)
{
    nmeap_rmc_t *rmc = (nmeap_rmc_t *)data;
    print_rmc(rmc);
}

/** called when a gpgga message is received and parsed */
static void gpgga_callout(nmeap_context_t *context,void *data,void *user_data)
{
    nmeap_gga_t *gga = (nmeap_gga_t *)data;
    print_gga(gga);
}

void deleteChar(char * str, int pos) {
	if (pos>=strlen(str))
		return;
	for (int i=pos; i<strlen(str); i++)
		str[i]=str[i+1];
}

void insertChar(char * str, int pos, char ch) {
	int len=strlen(str);
	if (pos>len)
		return;
	str[len+1]=0;
	for (int i=len; i>=pos; i--)
		str[i+1]=str[i];
	str[pos]=ch;	
}

void toUpperCase(char * str) {
	char *ptr=str;
	for(ptr=str;*ptr;ptr++)
   {
       if (*ptr >= 97 && *ptr <= 122) /* a-z */
       {
           *ptr-=32;
       }
   }

}


void initLatLon() {
	static int c = 256;
 	for (int d = 17; d >= 0; --d) { 
                 pixelsPerLonDegree[d] = c / 360;
                 pixelsPerLonRadian[d] = c / bc;
                 double e = c / 2;
                 bitmapOrigo[d] = c/2;
                 numTiles[d] = c / 256;
                 c *= 2;
	}
}




Coord getLatLong(int x, int y, int zoom) {
      double lon      = -180; // x
      double lonWidth = 360; // width 360

      double lat       = -1;
      double latHeight = 2;

      int tilesAtThisZoom = 1 << (17 - zoom);
      lonWidth  = 360.0 / tilesAtThisZoom;
      lon       = -180 + (x * lonWidth);
      latHeight = -2.0 / tilesAtThisZoom;
      lat       = 1 + (y * latHeight);

      // convert lat and latHeight to degrees in a transverse mercator projection
      // note that in fact the coordinates go from about -85 to +85 not -90 to 90!
      latHeight += lat;
      latHeight = (2 * atan(exp(PI * latHeight))) - (PI / 2);
      latHeight *= (180 / PI);

      lat = (2 * atan(exp(PI * lat))) - (PI / 2);
      lat *= (180 / PI);

      latHeight -= lat;

      if (lonWidth < 0) {
         lon      = lon + lonWidth;
         lonWidth = -lonWidth;
      }

      if (latHeight < 0) {
         lat       = lat + latHeight;
         latHeight = -latHeight;
      }

	Coord c;
	c.lat=lat;
	c.lon=lon;
	c.lonWidth=lonWidth;
	c.latHeight=latHeight;
      return c;
   }


//puts x value in Coord.lon and y value in Coord.lat
Coord getTileCoord(double lat, double lon, int zoomLevel ) {
	int x,y;
	 x= (int)(bitmapOrigo[zoomLevel] + lon * pixelsPerLonDegree[zoomLevel]);
	 x/=256;
                double e = sin(lat * Wa);

                if (e > 0.9999) {
                        e = 0.9999;
                }

                if (e < -0.9999) {
                        e = -0.9999;
                }

                y = (int)(bitmapOrigo[zoomLevel] + 0.5
                                * log((1 + e) / (1 - e)) * -1
                                * (pixelsPerLonRadian[zoomLevel]));
		y/=256;

		Coord cc;
		cc.lon=x;
		cc.lat=y;
		return cc;

} 

//puts x value in Coord.lon and y value in Coord.lat
Coord getXY(double lat, double lon, int zoomLevel ) {
	Coord c;
	int x,y;
	 x= (int)(bitmapOrigo[zoomLevel] + lon * pixelsPerLonDegree[zoomLevel]);
	 x/=256;
                double e = sin(lat * Wa);

                if (e > 0.9999) {
                        e = 0.9999;
                }

                if (e < -0.9999) {
                        e = -0.9999;
                }

                y = (int)(bitmapOrigo[zoomLevel] + 0.5
                                * log((1 + e) / (1 - e)) * -1
                                * (pixelsPerLonRadian[zoomLevel]));
		y/=256;
		c=getLatLong(x,y,basezoom);

		c.lon=(x-ftx)*TILE_SIZE+(lon-c.lon)*TILE_SIZE/c.lonWidth;
		c.lat=(y-fty)*TILE_SIZE+(c.lat-lat)*TILE_SIZE/c.latHeight+TILE_SIZE;
		return c;

} 

void readAttractionsListings() {
	optcount=0;
  	DIR *dp;
  	struct dirent *dir;
	if (datatype==1)
		optcount=unzipReadDir( zipfile , options);
	else {
		if ( (dp = opendir(zipfile)) == NULL ) {
    			printf("Can't open directory: %s\n", zipfile);
    			exit(1);
  		}
  	while ((dir = readdir(dp)) != NULL) {
        	if (dir->d_name[0]!='_') continue;
    		//fprintf(stdout,"%s\n", dir->d_name);
    		strcpy(options[optcount++],dir->d_name);
  }
  closedir(dp);
	}
}

void readMapListings( ) {
  DIR *dp;
  struct dirent *dir;
 optcount=0;
  if ( (dp = opendir("./maps")) == NULL ) {
    fprintf(stderr, "Can't open directory: ");
    exit(1);
  }
  while ((dir = readdir(dp)) != NULL) {
	if (dir->d_name[0]!='_') continue;
    //fprintf(stdout,"%s\n", dir->d_name);
    strcpy(options[optcount],dir->d_name);
    optcount++;
  }
  strcpy(options[optcount++],"===_WIFI_MAP_UPLOAD_===");
  closedir(dp);
}

#ifdef SDL_SOUND
void initSound() {
  int audio_rate;
  Uint16 audio_format;
  int audio_channels;
  int audio_buffers;

  if ( SDL_InitSubSystem(SDL_INIT_AUDIO) < 0 ) {
    	printf( "Unable to initialize SDL_AUDIO: %s\n", SDL_GetError());
    return;
  }

  audio_rate = 44100;
  audio_format = AUDIO_S16;
  audio_channels = 2;
  audio_buffers = 1024;

  if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) < 0) {
    	printf( "Couldn't open audio: %s\n", SDL_GetError());
    return;
  } else {
    	Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
  }

	chunk = Mix_LoadWAV("system/beep.wav");
}


void beep(){	
	Mix_PlayChannel(0, chunk, 0);
}
#else
void beep(){};
#endif

int attractions_menu(char * title, char options[][60] , int size, int cur ) {
        int i, timer=21;
	char filename[128];
	sceCtrlSetSamplingCycle(0);
        sceCtrlSetSamplingMode(1);

        while (!(cpad.Buttons & PSP_CTRL_CROSS)) {
	 	sceCtrlPeekBufferPositive(&cpad, 1); 
                sceKernelDelayThread(1);

		if (cpad.Buttons & PSP_CTRL_SELECT)
			return -1;
		if (cpad.Buttons & PSP_CTRL_START)
			helpscreen();

		if ((cpad.Buttons & PSP_CTRL_DOWN || cpad.Ly >= 0xD0) && timer>20) {
                        if (cur<size-1) {
                                cur++;
				beep();
			}	
                        timer=0;
                }

        	if ((cpad.Buttons &  PSP_CTRL_UP || cpad.Ly <= 0x30) && timer>20) {
                        timer=0;
                        if (cur>0) {
                                cur--;
				beep();
			}
                }


                timer++;
                if (timer>100) timer=21;
                draw_string( title, FONT01,PSP_WIDTH/2-strlen(title)*FONT01W/2, 34);
               	draw_string( "                                                       ", FONT02,20, cur*12+71);
                for (i=0; i<size; i++) {
			strcpy(filename,options[i]);
			toUpperCase(filename);
                	draw_string( filename, FONT01,30, i*12+71);

                }
		flipScreen();
                sceKernelDelayThread(5);


        }
	return cur;
}

void pad(char * str) {
	for (int i=0; i<strlen(str); i++) {
		if (str[i]==' ' || str[i]=='/' || str[i]=='\\' || str[i]=='?' || str[i]=='<' || str[i]=='>')
			str[i]='_';

	}
}

int check_fields(char * name, double lat, double lon, int bz, int sz) {
	pad (name);
	if (lat==0.0 || lon==0.0)
		return -1;
	if (bz<0 || bz>14)
		return -1;
	if (sz!=4 && sz!=8 && sz!=16 && sz!=32 && sz!=64 && sz!=128 && sz!=256 && sz!=512) 
		return -1;
	return 1;

}

int map_upload_menu() {

	int current=0;
	char headers[5][16] = { "MAP NAME" , "LATITUDE", "LONGITUDE", "BASE ZOOM", "MAP SIZE" };
	char notes[5][64] = { "NOTE: SOME CHARACTERS ARE NOT SUPPORTED" , "NOTE: MUST BE IN DECIMAL FORMAT", "NOTE: MUST BE IN DECIMAL FORMAT", "RANGE: 0-14 (0 IS MOST DETAILED)", "MUST BE 4,8,16,32,64,128,256" };
	int lens[5]= { 40,10,10,2,3};
	char values[5][64];
    	Coord co=getGPS(mapx, mapy);
	sprintf(values[0],"MAP_FOR_LAT_%10.6f__LON_%10.6f",co.lat, co.lon);
	sprintf(values[1],"%f",co.lat);
	sprintf(values[2],"%f", co.lon);
	sprintf(values[3],"2");
	sprintf(values[4],"16");
	int edit_pos=0;
	char error[32];
	strcpy( error,"");
	sceCtrlSetSamplingCycle(0);
        sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
	Image * menuwindow=loadImage("system/menuwindow.png");
	Image * background=loadImage("system/background.png");
	danzeff_load(); 
	if (!(danzeff_isinitialized())) { return 0; } 
	danzeff_moveTo(300,96);
	int blinkcounter=0;
        while (1) {
		sceCtrlPeekBufferPositive(&cpad, 1); 
                sceKernelDelayThread(1);
		if (background!=NULL)
			blitAlphaImageToScreen(0,0,PSP_WIDTH,PSP_HEIGHT,background,0,0);
		if (menuwindow!=NULL)
			blitAlphaImageToScreen(0,0,455,232,menuwindow,12,28);
		else
			fillScreenRect(0x000000cc,12,28,455,232);
                draw_string( "::: WIFI MAP UPLOAD FORM :::", FONT01,PSP_WIDTH/2-26*FONT01W/2, 34);
               	draw_string( "EDIT THE FIELDS BELOW AND PRESS [START] BUTTON", FONT01,58, 58);
		for (int i=0; i<5; i++) {
               		draw_string( headers[i], FONT01,20, 83+15*i);
			if (i==current) {
				for (int j=0; j<strlen(values[i])+2; j++) {
               				draw_string( " ", FONT02,102+j*8, 79+15*i);
               				draw_string( " ", FONT02,102+j*8, 87+15*i);
				}
				if (blinkcounter++>10) 
               				draw_string( "-", FONT01,110+edit_pos*8, 89+15*i);
				if (blinkcounter++>18)
					blinkcounter=0;
			}
               		draw_string( values[i], FONT01,110, 83+15*i);
		}
               	draw_string( error, FONT01,35,200 );
	
               	draw_string( notes[current], FONT01,15,248 );
    		danzeff_render(); 
		char key= danzeff_readInput(cpad);

		if (key==DANZEFF_START) {
			if (check_fields(values[0], atof(values[1]), atof(values[2]), atoi(values[3]), atoi(values[4]))==1) 
				break;
			else
				strcpy(error,"INCORRECT VALUES ENTERED!!!");


		}

		switch (key) 
               { 
          case 0: 
          break; 
         case 9: if (current>0) {
			current--;
			edit_pos=0;
		}
		strcpy( error,"");
		break; 
         case '\n': if (current<4) {
			current++; 
			edit_pos=0;
		}
		strcpy( error,"");
		break;
         case DANZEFF_LEFT: if (edit_pos>0) edit_pos--;
		strcpy( error,"");
		break; 
         case DANZEFF_RIGHT: if (edit_pos<lens[current]) edit_pos++;
		strcpy( error,"");
		break;
         case DANZEFF_SELECT: 
		if (menuwindow!=NULL)
			freeImage(menuwindow);
		if (background!=NULL)
			freeImage(background);
		danzeff_free();
		return 0;
         case DANZEFF_START: break; 
	case 8: //delete
		strcpy( error,"");
		deleteChar(values[current],edit_pos);
		break;
         default: 
		strcpy( error,"");
		if (strlen(values[current])<lens[current]) {
			insertChar(values[current],edit_pos,key);
			edit_pos++;
		}
		toUpperCase(values[current]);
             fprintf(stdout,"You have input the key %c\n",key); 
             break; 
       }
		flipScreen();

	}

	danzeff_free();
	sprintf (dirname,"maps/_%s", values[0]);
	latitude=atof(values[1]);
	longitude=atof(values[2]);
	basezoom=atoi(values[3]);
	mapsize=atoi(values[4]);
	memset(messages,0,sizeof(messages));
// start upload thread 
	char tname[32];
	u64 tick;	
	sceRtcGetCurrentTick(&tick);
	sprintf(tname,"WIFI#%ld",tick);
	if (wifithid>0)
		sceKernelTerminateDeleteThread(wifithid);
   	wifithid = sceKernelCreateThread(tname, wget, 0x11, 512 * 1024, PSP_THREAD_ATTR_USER, NULL);
	fprintf(stdout,"%d\n",wifithid);
   	sceKernelStartThread(wifithid, 0, NULL);

        while (!(cpad.Buttons & PSP_CTRL_SELECT)) {
	 	sceCtrlPeekBufferPositive(&cpad, 1); 
                sceKernelDelayThread(1);
		if (background!=NULL)
			blitAlphaImageToScreen(0,0,PSP_WIDTH,PSP_HEIGHT,background,0,0);
		if (menuwindow!=NULL)
			blitAlphaImageToScreen(0,0,455,232,menuwindow,12,28);
		else
			fillScreenRect(0x000000cc,12,28,455,232);
                draw_string( "::: WIFI MAP UPLOAD STATUS :::", FONT01,PSP_WIDTH/2-26*FONT01W/2, 34);

		for (int i=0; i<15; i++)
                	draw_string( messages[i], FONT01,30, 50+i*12);
			
               	draw_string( "PRESS [SELECT] TO CANCEL", FONT01,15,248 );

		flipScreen();
	}

	killwifi=1;


	if (menuwindow!=NULL)
		freeImage(menuwindow);
	if (background!=NULL)
		freeImage(background);
}

int display_menu(char * title, char options[][60] , int size, int cur ) {
        int i, timer=11;
	char filename[128];
	sceCtrlSetSamplingCycle(0);
        sceCtrlSetSamplingMode(1);

	Color * bg= (Color *) malloc ( FRAMEBUFFER_SIZE);
	memcpy(bg, getVramDrawBuffer(), FRAMEBUFFER_SIZE);

	Image * menuwindow=loadImage("system/menuwindow.png");

        while (!(cpad.Buttons & PSP_CTRL_CROSS)) {
	 	sceCtrlPeekBufferPositive(&cpad, 1); 
                sceKernelDelayThread(1);


		if (cpad.Buttons & PSP_CTRL_SELECT)
			return -1;
		if (cpad.Buttons & PSP_CTRL_START)
			helpscreen();

		if ((cpad.Buttons & PSP_CTRL_DOWN || cpad.Ly >= 0xD0) && timer>10) {
                        if (cur<size-1) {
                                cur++;
				beep();
			}	
                        timer=0;
                }

        	if ((cpad.Buttons &  PSP_CTRL_UP || cpad.Ly <= 0x30) && timer>10) {
                        timer=0;
                        if (cur>0) {
                                cur--;
				beep();
			}
                }


                timer++;
                if (timer>100) timer=11;
		if (bg!=NULL)
			memcpy( getVramDrawBuffer(),bg, FRAMEBUFFER_SIZE);
		if (menuwindow!=NULL)
			blitAlphaImageToScreen(0,0,455,232,menuwindow,12,28);
		else
			fillScreenRect(0x000000cc,12,28,455,232);
                draw_string( title, FONT01,PSP_WIDTH/2-strlen(title)*FONT01W/2, 34);
               	draw_string( "                                                       ", FONT02,20, cur*15+57);
               	draw_string( "                                                       ", FONT02,20, cur*15+65);
                for (i=0; i<size; i++) {
			strcpy(filename,options[i]);
			toUpperCase(filename);
                	draw_string( filename, FONT01,30, i*15+61);

                }
		flipScreen();
                sceKernelDelayThread(5);


        }

	if (bg!=NULL)
		free(bg);
	if (menuwindow!=NULL)
		freeImage(menuwindow);

	return cur;
}




void load_coords(char* zipfile) {
        FILE *fp;
        char line[60];
        char xstr[30], ystr[30];
	Coord c;
        int rd=0;
	basezoom=2;
	if (datatype==1) {
        	unzipFile(zipfile,"coords.txt","coords.txt");
        	fp = fopen("coords.txt", "r");
	} else {
		sprintf(line, "%s/coords.txt",zipfile);
		 fp = fopen(line, "r");
	}
        if (fp==NULL) {
                printf("load_coords:::couldn't open file: %s\n", "coords.txt");
		lat=lon=lat1=lon1=0;
                return;

        }

        if(fgets(line,60,fp) != NULL) {
                rd=sscanf(line,"%s %s %d",  xstr, ystr , &basezoom);
                if (rd<3) {
                        printf("load_coords:::couldn't read all values %d\n", rd);
			lat=lon=0;
                } else {
			ftx=atof(xstr);
			fty=atof(ystr);

                	c=getLatLong(atoi(xstr),atoi(ystr),basezoom);
			printf("lat=%f lon=%f lh=%f lw=%f\n", c.lat, c.lon,c.latHeight, c.lonWidth);
		}

        } else
		lat=lon=0;


        fclose(fp);

	if (datatype==1)
        	remove("coords.txt");
}


Coord getGPS (int x, int y) {
	Coord c = getLatLong((int)(x/TILE_SIZE+ftx),(int)(y/TILE_SIZE+fty-1),basezoom);
	c.lon=c.lon+x%TILE_SIZE*c.lonWidth/TILE_SIZE;
	c.lat=c.lat-y%TILE_SIZE*c.latHeight/TILE_SIZE;
	return c;
}
/*
//puts x value in Coord.lon and y value in Coord.lat
Coord getXY(double lat, double lon) {
	int x=ftx;
	int y=fty-1;
	Coord c;

	c=getLatLong(x,y,basezoom);
	while (c.lon<lon) 
		c=getLatLong(++x,y,basezoom);
	while (c.lat>lat) 
		c=getLatLong(x,++y,basezoom);
	x--;
	y--;
	c=getLatLong(x,y,basezoom);

	c.lon=(x-ftx)*TILE_SIZE+(lon-c.lon)*TILE_SIZE/c.lonWidth;
	c.lat=(y-fty)*TILE_SIZE+(c.lat-lat)*TILE_SIZE/c.latHeight+TILE_SIZE;
	return c;
}
*/



int detect_size(char *zipfile) {
	int maxsize=512;
	char filename[128];
	Image * last = NULL;
	Image * first = NULL;
	//check png vs jpg
	if (datatype==1) {
		sprintf(filename, "1x/000/1x000000.jpg");
		first=loadfromzip(filename,zipfile);
	} else {
		sprintf(filename, "%s/1x/000/1x000000.jpg",zipfile);
		first=loadfromdir(filename);
	}

	if (first==NULL) {
		strcpy(filetype	, "png");
	} else {
		strcpy(filetype	, "jpg");
		unloadmap(filename);
	}

	while (last == NULL) {
		maxsize=maxsize/2;
		if (datatype==1) {
			sprintf(filename, "1x/%03d/1x%03d%03d.%s",(maxsize-1),(maxsize-1),(maxsize-1),filetype);
			last=loadfromzip(filename,zipfile);
		} else {
			sprintf(filename, "%s/1x/%03d/1x%03d%03d.%s",zipfile,(maxsize-1),(maxsize-1),(maxsize-1),filetype);
			last=loadfromdir(filename);
		}
	}
	unloadmap(filename);
	return maxsize;
}

int get_zoom(int tile_num) {
	int tn=tile_num;
	int power=0;
	while (tn!=1) {
		tn=tn/2;
		power++;
	}
	return power;
}

int powerOf(int p){
	int two=1;
	for (int i=0; i<p; i++)
		two*=2;
	return two;
}


static void cleanup_output(void)
{
        FILE *file;
        int empty;

        /* Flush the output in case anything is queued */
        fclose(stdout);
        fclose(stderr);

        /* See if the files have any output in them */
        file = fopen(STDOUT_FILE, "rb");
        if ( file ) {
                empty = (fgetc(file) == EOF) ? 1 : 0;
                fclose(file);
                if ( empty ) {
                        remove(STDOUT_FILE);
                }
        }
        file = fopen(STDERR_FILE, "rb");
        if ( file ) {
                empty = (fgetc(file) == EOF) ? 1 : 0;
                fclose(file);
                if ( empty ) {
                        remove(STDERR_FILE);
                }
        }
}


/*

int gps_comm (SceSize args, void *argp) {
	int jj=2;
	//pspDebugSioInit();
    	//pspDebugSioSetBaud(baud);
	status = nmeap_init(&nmea,(void *)&user_data);
    	if (status != 0) {
        	printf("nmeap_init %d\n",status);
        	exit(1);
    	}
    
    status = nmeap_addParser(&nmea,"GPGGA",nmeap_gpgga,gpgga_callout,&gga);
    if (status != 0) {
        printf("nmeap_add %d\n",status);
        exit(1);
    }

    status = nmeap_addParser(&nmea,"GPRMC",nmeap_gprmc,gprmc_callout,&rmc);
    if (status != 0) {
        printf("nmeap_add %d\n",status);
        exit(1);
    }
	int ch;
	while(jj<0)   //exit right away for now
	{
		ch = pspDebugSioGetchar();

		if (ch>0) {
			printf("%c",ch);
			status = nmeap_parse(&nmea,ch);
        		switch(status) {
        			case NMEAP_GPGGA:
            				print_gga(&gga);
            				break;
        			case NMEAP_GPRMC:
            				print_rmc(&rmc);
            				break;
        			default:
            				break;
        		} 
		} 
			//sceKernelDelayThread(1);
	}
}

*/


int readchar(char *pvec)
{
    int ch;
    if (*pvec == 0) {
        ch = -1;
    }
    else {
        ch = *pvec++;
    }
    return ch;
}


void getGPSMessageFromFile() {
	u64 newTick;
	sceRtcGetCurrentTick(&newTick);
	if (newTick-currTick<900000)
		return;
	if (fp==NULL)
		return;
	char line[256];
	char * s;
	int ch;
	int status;
		for (int i=0; i<2; i++) {
		s=fgets(line,256,fp);
		while (strncmp(line,"$GPGGA",6)!=0 && strncmp(line,"$GPRMC",6)!=0 && s!=NULL)
			s=fgets(line,255,fp);
		 line[strlen(line)+1]=0;

		//fprintf(stdout,"[%s]\n",line);
		char *pvec =line;
		ch=0;
		while (ch>=0) {
			if (*pvec == 0) 
        			ch = -1;
    			else 
        			ch = *pvec++;
			//printf("%c",ch);
			status = nmeap_parse(&nmea,ch);
		}
		}

		


}
/*
void getGPSMessage() {
	int ch;
	gpsdone=0;
	u64 newTick;
	sceRtcGetCurrentTick(&newTick);
	if (newTick-currTick<600000)
		return;
	while(!gpsdone ) {
		ch = pspDebugSioGetchar();
	 	sceCtrlPeekBufferPositive(&cpad, 1); 
		if (cpad.Buttons!=34078720 || cpad.Ly >= 0xD0 || cpad.Ly <= 0x30|| cpad.Lx <= 0x30 || cpad.Lx >= 0xD0)
			gpsdone++;
		if (ch>0) {
			//fprintf(stdout,"%c",ch);
			status = nmeap_parse(&nmea,ch);
		} 
	}
}
*/

void getGPSMessage() {
	int ch;
	int loops=0;
	gpsdone=0;
	u64 newTick;
	sceRtcGetCurrentTick(&newTick);
	if (newTick-currTick<500000)
		return;
	ch = pspDebugSioGetchar();

	//lets wait for first non-empty char

	while (ch<=0 && !gpsdone) {
		ch = pspDebugSioGetchar();
	 	sceCtrlPeekBufferPositive(&cpad, 1); 
		if (cpad.Buttons!=34078720 || cpad.Ly >= 0xD0 || cpad.Ly <= 0x30|| cpad.Lx <= 0x30 || cpad.Lx >= 0xD0)
			return;

	}

	char nmeabuf[512];
	memset (nmeabuf,0, sizeof(nmeabuf));
	int len=0;
	while(!gpsdone  && loops<1000 && len<1020) {
	 	sceCtrlPeekBufferPositive(&cpad, 1); 
		if (cpad.Buttons!=34078720 || cpad.Ly >= 0xD0 || cpad.Ly <= 0x30|| cpad.Lx <= 0x30 || cpad.Lx >= 0xD0)
			gpsdone++;
		if (ch>0) {
			//fprintf(stdout,"%c",ch);
			//status = nmeap_parse(&nmea,ch);
			nmeabuf[len++]=ch;
			loops=0;
		} else
			loops++;

			
		ch = pspDebugSioGetchar();
	}
	if (gpsout!=NULL)
		fprintf(gpsout, "%s\n", nmeabuf);
	for (int i=0; i<len; i++) 
		 nmeap_parse(&nmea,nmeabuf[i]);


	sceRtcGetCurrentTick(&currTick);
}


int garbage_collector (SceSize args, void *argp) {
	char filename[128]; 
	int i=2;
	int tx, ty;
	int oldzoom=zm;
	int zoom;

	
	while (i>0) {

		if (!idle) {
		for ( tx=0; tx<TILE_SIZE*TILE_NUM/zm; tx+=TILE_SIZE ) {
			for ( ty=0; ty<TILE_SIZE*TILE_NUM/zm; ty+=TILE_SIZE ) {
				sceKernelDelayThread(2);
				if (zm!=oldzoom)
					break;
				if (datatype==1)
					sprintf(filename,"%dx/%03d/%dx%03d%03d.%s", zm, (int)ty/TILE_SIZE,zm, (int)ty/TILE_SIZE, (int) tx/TILE_SIZE,filetype);
				else
					sprintf(filename,"%s/%dx/%03d/%dx%03d%03d.%s",zipfile, zm, (int)ty/TILE_SIZE,zm, (int)ty/TILE_SIZE, (int) tx/TILE_SIZE,filetype);
				if (tx < mapx/zm - PSP_WIDTH/2 - TILE_SIZE-RADIUS ||  tx > mapx/zm + PSP_WIDTH/2+RADIUS || ty < mapy/zm - PSP_HEIGHT - TILE_SIZE-RADIUS || ty > mapy/zm + PSP_HEIGHT+RADIUS) 
					 unloadmap(filename);
					
			}
			if (zm!=oldzoom) {
				for (zoom=1; zoom<TILE_NUM; zoom*=2) {
					if (zoom!=zm)
						unload_group(zoom,zipfile);
				}
				oldzoom=zm;
				break;
			} 
		}
	tlock=1;
        imglist_garbagecollect();
        tlock=0;
	}

	sceKernelDelayThread(10);
	}
	return 0;
}

int cachemngr (SceSize args, void *argp) {
	int i=2;
	int tx;
	int ty;
	int tyfix;
	int topx, topy; 
	int oldzoom=zm;
	char filename[128]; 

	printf("cache mngr started\n");
	while (i>0) {
		if (!idle) {
		tx=  (mapx/zm - PSP_WIDTH/2 - TILE_SIZE-RADIUS)/TILE_SIZE*TILE_SIZE;
		ty=  (mapy/zm - PSP_HEIGHT/2 - TILE_SIZE-RADIUS)/TILE_SIZE*TILE_SIZE;
		if (tx<0) tx=0;
		if (ty<0) ty=0;
		topx=tx+TILE_SIZE*4;
		if (topx>TILE_NUM*TILE_SIZE/zm)
			topx=TILE_NUM*TILE_SIZE/zm;
		topy=ty+TILE_SIZE*4;
		if (topy>TILE_NUM*TILE_SIZE/zm)
			topy=TILE_NUM*TILE_SIZE/zm;

		tyfix=ty;



	while (tx < topx ) {
		ty=tyfix;
		while (ty < topy ) {
			if (zm!=oldzoom)
				break;
			sceKernelDelayThread(3);
			if (datatype==1)
				sprintf(filename,"%dx/%03d/%dx%03d%03d.%s",zm, (int)ty/TILE_SIZE, zm, (int)ty/TILE_SIZE, (int) tx/TILE_SIZE,filetype);
			else
				sprintf(filename,"%s/%dx/%03d/%dx%03d%03d.%s",zipfile, zm, (int)ty/TILE_SIZE, zm, (int)ty/TILE_SIZE, (int) tx/TILE_SIZE,filetype);
			if (tx > mapx/zm - PSP_WIDTH/2 - TILE_SIZE-RADIUS &&  tx < mapx/zm + PSP_WIDTH/2+RADIUS && ty > mapy/zm - PSP_HEIGHT - TILE_SIZE-RADIUS && ty < mapy/zm + PSP_HEIGHT+RADIUS)  {
				if (datatype==1)
					loadfromzip_check4blank(filename,zipfile);
				else
					loadfromdir_check4blank(filename);
			} else
				unloadmap(filename);
			ty+=TILE_SIZE;
		}
		if (zm!=oldzoom) {
			oldzoom=zm;
			break;
		}
		
		tx+=TILE_SIZE;
	}

	}
	sceKernelDelayThread(3);
	}
	return 0;
}


 /* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
	cleanup_output();
        sceKernelExitGame();
        return 0;
}

/* Callback thread */
int CallbackThread(SceSize args, void *argp)
{
        int cbid;

        cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
        sceKernelRegisterExitCallback(cbid);

        sceKernelSleepThreadCB();

        return 0;
}

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void)
{
        int thid = 0;

        thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
        if(thid >= 0)
        {
                sceKernelStartThread(thid, 0, 0);
        }

        return thid;
}


void draw_zoom_scale(int x,int y,Image * zs, int zoom , int maxzoom) {
	int i=0;
	if (maxzoom<2)
		return;
	blitAlphaImageToScreen(0,0,19,3,zs,x,y);
	for (i=0; i<maxzoom; i++) {
		blitAlphaImageToScreen(0,3,19,12,zs,x,y+3+11*i);
	}
	blitAlphaImageToScreen(0,16,19,10,zs,x,y+3+11*(zoom-1));

}


void turn_off_attractions() {
	attr_flag=0;
	if (datatype==1) {
		if (attraction!=NULL)
			unloadmap(attr_ifile);
	} else {
		if (attraction!=NULL) {
			freeImage(attraction);	
			attraction=NULL;
		}
	}
}


#ifndef SDL_SOUND 
int main( int argc, char *argv[] )
#else
extern "C" int SDL_main( int argc, char *argv[] )
#endif

{
	int j;
	char printbuf[64];
	char filename[128];
	char attrbuf1[64];
	char attrbuf2[64];
	char attr_file[64];
	int tx,ty, tyfix, tmpx,tmpy, cxoffset=0, cyoffset=0, timer=21;
	int frame=0;
	freopen(STDOUT_FILE, "w", stdout);
	freopen(STDERR_FILE, "w", stderr);
	readConfig();
	//if (checkForMAC(checksum)!=1) {
       //	sceKernelExitGame();
	//	return 0; 
	//}
	int counter=0;

	Image * cursor, *blank, *scale, *zoomscale, *arrows, *compass;
#ifndef SDL_SOUND
	pspDebugSioInit();
    	pspDebugSioSetBaud(baud);
	pspSdkLoadInetModules();
#endif

	scePowerSetClockFrequency(333,333,166);
        SetupCallbacks();
	initGraphics();
	font_init() ;
	splashscreen();

	
	status = nmeap_init(&nmea,(void *)&user_data);
    	if (status != 0) {
        	printf("nmeap_init %d\n",status);
        	exit(1);
    	}
    
    	status = nmeap_addParser(&nmea,"GPGGA",nmeap_gpgga,gpgga_callout,&gga);
    	if (status != 0) {
        	printf("nmeap_add %d\n",status);
        	exit(1);
    	}

    	status = nmeap_addParser(&nmea,"GPRMC",nmeap_gprmc,gprmc_callout,&rmc);
    	if (status != 0) {
        	printf("nmeap_add %d\n",status);
        	exit(1);
    	}

#ifdef SDL_SOUND
	initSound() ;
#endif
	initLatLon();
	Coord c,xy;
	rotimage=createImage(512,512);
	while (1) {
	if (fakefeed)
		fp=fopen("gps.txt","r");
	topmenu:
	readMapListings(); 
	Image * background=loadImage("system/background.png");
	blitAlphaImageToScreen(0,0,PSP_WIDTH,PSP_HEIGHT,background,0,0);
        int filenum =display_menu("::: SELECT MAP :::", options, optcount, 0 ) ;
	beep();
	fprintf(stdout,"[%s]\n",options[filenum]);
	if (strcmp(options[filenum],"===_WIFI_MAP_UPLOAD_===")==0) {
		map_upload_menu();
		goto topmenu;
	}
	if (background!=NULL)
		freeImage(background);
	strcpy(attr_file,"NONE");
	attr_width=16;
	attr_height=8;
	gpsOn=0;
	rotatemap=0;
	strcpy(attr_ifile,"");


	if (filenum==-1)
		break;
	sprintf(zipfile,"./maps/%s",options[filenum]);

	if (zipfile[strlen(zipfile)-4]=='.')
		datatype=1;
	else
		datatype=0;



	TILE_NUM=detect_size(zipfile);
	load_coords(zipfile);

	maxzoom=zoom=get_zoom(TILE_NUM);
	zm = powerOf(zoom-1);
	mapx = TILE_SIZE*TILE_NUM/2;
	mapy=mapx;
	oldy=oldx=mapx;

	idle=0;


	if (firsttime==1) {
/*
	int gpsid = 0;
        gpsid = sceKernelCreateThread("gps_comm_thread",gps_comm, 40, 20000, 0, 0);
        if(gpsid >= 0)
        {
                sceKernelStartThread(gpsid, 0, 0);
        }
*/

	int gid = 0;
        gid = sceKernelCreateThread("garbage_collector_thread",garbage_collector, 34, 2000, 0, 0);
        if(gid >= 0)
        {
                sceKernelStartThread(gid, 0, 0);
        }

	int thid = 0;
        thid = sceKernelCreateThread("cachemngr_thread", cachemngr, 33, 4000, 0, 0);
        if(thid >= 0)
        {
                sceKernelStartThread(thid, 0, 0);
        }


	cursor = loadImage("system/cursor.png");
	arrows = loadImage("system/arrows.png");
	compass = loadImage("system/compass.png");
	blank = loadImage("system/blank.png");
	scale = loadImage("system/scale.png");
	zoomscale = loadImage("system/zoomscale.png");

	sceCtrlSetSamplingCycle(0);
	sceCtrlSetSamplingMode(1);
	firsttime=0;
	}


	
	while (!(cpad.Buttons & PSP_CTRL_SELECT)) {
	timer++;
	if (timer>100) timer=21;

	 sceCtrlPeekBufferPositive(&cpad, 1); 

	if (cpad.Buttons & PSP_CTRL_START)
		helpscreen();
  
  	if (cpad.Ly <= 0x30)  {
		gpsOn=0;
		rotatemap=0;
		if (mapy>0)
    			mapy-=zm;
	}
  	if (cpad.Ly >= 0xD0) {
		gpsOn=0;
		rotatemap=0;
		if (mapy<TILE_SIZE*TILE_NUM)
    			mapy+=zm;
	}
  	if (cpad.Lx <= 0x30) {
		gpsOn=0;
		rotatemap=0;
		if (mapx>0)
    			mapx-=zm;
	}
  	if (cpad.Lx >= 0xD0) {
		gpsOn=0;
		rotatemap=0;
		if (mapx<TILE_SIZE*TILE_NUM)
    			mapx+=zm;
	}
	if ((cpad.Buttons &  PSP_CTRL_SQUARE) && timer>20) {
		timer=0;
		gpsOn=1;
		strcpy(warn,"BAD");
		mx=mapx;
		my=mapy;
		deltax=0;
		deltay=0;
	        display_message("","G P S   M O D E   O N","",800);
	}

	if ((cpad.Buttons &  PSP_CTRL_START) && timer>20) {
		timer=0;
		helpscreen();
	}


	if ((cpad.Buttons &   PSP_CTRL_CIRCLE) && timer>20) {
		timer=0;
		beep();
		readAttractionsListings();
		strcpy(options[optcount++],"NONE");
		int fn=display_menu("::: SELECT ATTRACTION TYPE :::",  options , optcount, 0 ); 
		for (int k=0; k<optcount; k++)
			printf("attractions files: %s %d\n",options[k],fn);

		strcpy(attr_file,options[fn]);

	}

	if ((cpad.Buttons &   PSP_CTRL_TRIANGLE) && timer>20) {
		timer=0;
		beep();

		if (zm==0){ // turned off
	        display_message("ATTRATIONS DATA IS ONLY","","VIEWABLE AT BASE ZOOM",800);
		} else {
		if (attr_flag==1) {
			turn_off_attractions();
	        	display_message("","A T T R A C T I O N S   O F F","",800);
			free_attractions();
		} else {
	       	display_message(""," L O A D I N G   D A T A ","",0);
		if (attr_file!=NULL && strcmp(attr_file,"NONE")!=0) {
			int attr_counter=load_attractions(attr_file, zipfile, datatype,  mapx,  mapy, zoom);
			if (attr_counter>998) {
	       			display_message(" TOO MANY ATTRACTIONS ","FOUND IN THIS REGION!","ZOOM IN AND TRY AGAIN",2000);
				turn_off_attractions();
				free_attractions();
			}
			
			//printf("!!attractions:%s::%s::%d::%d\n", attr_file, attr_ifile, attr_width, attr_height);

		if (strlen(attr_ifile)==0)
                        attraction = loadImage("system/attraction.png");
                else {
                        if (datatype==1)
                                attraction=loadfromzip(attr_ifile,zipfile);
                        else {
                                char filename1[128];
                                sprintf(filename1,"%s/%s", zipfile,attr_ifile);
                                attraction=loadImage(filename1);
                        }
                }

		attr_flag=1;
		} else {
	       		display_message(""," NO ATTRACTION TYPE SELECTED ","",1000);
		}
		}
		}
	}
	//toggle  gps nmea to file dump
	if ((cpad.Buttons &   PSP_CTRL_LEFT) && timer>20 &&!fakefeed) {
			beep();
			timer=0;
			if (gpsout!=NULL) {
	       			display_message("GPS  DATA",""," RECORDING  OFF  ",400);
				fclose(gpsout);
				gpsout=NULL;
			} else {
				gpsout=fopen("gps.txt", "w");
	       			display_message("GPS  DATA",""," RECORDING  ON  ",400);
			}

	}
	if ((cpad.Buttons &   PSP_CTRL_RIGHT) && timer>20 && gpsOn) {
			beep();
			timer=0;
			if (rotatemap) {
	       			display_message(" SWITCHING  TO ",""," NORTH  UP  MODE  ",800);
				rotatemap=0;
			} else {
				rotatemap=1;
	       			display_message(" SWITCHING  TO ",""," TRACK  UP  MODE  ",800);
			}

	}
	if ((cpad.Buttons &   PSP_CTRL_DOWN) && timer>20) {
		if (zm<TILE_NUM/2) {
			beep();
			timer=0;
			zoom++;
			zm = powerOf(zoom-1);
			turn_off_attractions();
			free_attractions();
		}
  	}

	if ((cpad.Buttons &  PSP_CTRL_UP) && timer>20) {
		if (zoom>1) {
			beep();
			timer=0;
			zoom--;
			zm = powerOf(zoom-1);
			turn_off_attractions();
			free_attractions();
		}

  	}


	tx=  (mapx/zm - PSP_WIDTH/2 - TILE_SIZE)/TILE_SIZE*TILE_SIZE;
	ty=  (mapy/zm - PSP_HEIGHT/2 - TILE_SIZE)/TILE_SIZE*TILE_SIZE;

	if (mapx/zm>TILE_SIZE*TILE_NUM/zm-PSP_WIDTH/2)
		tx-=TILE_SIZE;
	if (mapy/zm>TILE_SIZE*TILE_NUM/zm-PSP_HEIGHT/2)
		ty-=TILE_SIZE;

		tyfix=ty;
	while (tx < mapx/zm + PSP_WIDTH) {
		ty=tyfix;
		while (ty < mapy/zm + PSP_HEIGHT) {
			sceKernelDelayThread(1);
			if (datatype==1)
				sprintf(filename,"%dx/%03d/%dx%03d%03d.%s",zm, (int) ty/TILE_SIZE, zm, (int)ty/TILE_SIZE, (int) tx/TILE_SIZE,filetype);
			else
				sprintf(filename,"%s/%dx/%03d/%dx%03d%03d.%s",zipfile,zm, (int) ty/TILE_SIZE, zm, (int)ty/TILE_SIZE, (int) tx/TILE_SIZE,filetype);
			if (tlock==0)
				current=loadfromcache(filename);
			tmpx=tx-mapx/zm+PSP_WIDTH/2;
			 cxoffset=0;
			 cyoffset=0;
			if (mapx/zm<PSP_WIDTH/2) {
				tmpx=tx;
				cxoffset=mapx/zm-PSP_WIDTH/2;
			}

			if (mapx/zm>TILE_SIZE*TILE_NUM/zm-PSP_WIDTH/2) {
				tmpx=tx-TILE_SIZE*TILE_NUM/zm+PSP_WIDTH;
				cxoffset=mapx/zm-TILE_SIZE*TILE_NUM/zm+PSP_WIDTH/2;
			}

			tmpy=ty-mapy/zm+PSP_HEIGHT/2;
			if (mapy/zm<PSP_HEIGHT/2) {
				tmpy=ty;
				cyoffset=mapy/zm-PSP_HEIGHT/2;
			}
		
			if (mapy/zm>TILE_SIZE*TILE_NUM/zm-PSP_HEIGHT/2) {
				tmpy=ty-TILE_SIZE*TILE_NUM/zm+PSP_HEIGHT;
				cyoffset=mapy/zm-TILE_SIZE*TILE_NUM/zm+PSP_HEIGHT/2;
			}
		
			if (!rotatemap) {
			if (current!=NULL)
				blitAlphaImageToScreen(0,0,TILE_SIZE,TILE_SIZE,current,tmpx,tmpy);
			else {
				blitAlphaImageToScreen(0,0,TILE_SIZE,TILE_SIZE,blank,tmpx,tmpy);
				sceKernelDelayThread(5);
			}
			} else {
		//	fprintf(stdout,"%3d:%3d\n",tmpx, tmpy);

			if (current!=NULL)
				blitImageToImage(0,0,TILE_SIZE,TILE_SIZE,current,tmpx+16,tmpy+120,rotimage);
			else {
				blitImageToImage(0,0,TILE_SIZE,TILE_SIZE,blank,tmpx+16,tmpy+120,rotimage);

//				blitImageToImage(0,0,TILE_SIZE,TILE_SIZE,blank,0,0,rotimage);
				sceKernelDelayThread(5);
			}
			}

			ty+=TILE_SIZE;
				
		}
		tx+=TILE_SIZE;
	}

	if (rotatemap)
		rotate512img(rotimage,(180+course)*0.0174532925);
	counter++;
	if (counter>2) {
		c=getGPS(mapx,mapy);
		//xy=getTileCoord(c.lat, c.lon, basezoom);
		counter=0;
	}

	//display attractions....
	strcpy(attrbuf1,"");
	if (attr_flag==1  && !rotatemap) {
		for (j=0; j<attractions_count(); j++) {
			if (attractions[j]!=NULL && abs(attractions[j]->x-mapx)<PSP_WIDTH*zm && abs(attractions[j]->y-mapy) < PSP_HEIGHT*zm){
				blitAlphaImageToScreen(attr_width/2,0,attr_width/2,attr_height,attraction,(attractions[j]->x/zm-attr_width/4-mapx/zm)+PSP_WIDTH/2+cxoffset,(attractions[j]->y/zm-attr_height/2-mapy/zm)+PSP_HEIGHT/2+cyoffset);
				if (abs(attractions[j]->x/zm-mapx/zm)<attr_width/4 && abs(attractions[j]->y/zm-mapy/zm) < attr_height/2) {
					sprintf(attrbuf1,"POI: %15s: %38s",&attr_file[1],attractions[j]->name); 
					sprintf(attrbuf2,"%60s",attractions[j]->details); 
					blitAlphaImageToScreen(0,0,attr_width/2,attr_height,attraction,(attractions[j]->x/zm-attr_width/4-mapx/zm)+PSP_WIDTH/2+cxoffset,(attractions[j]->y/zm-attr_height/2-mapy/zm)+PSP_HEIGHT/2+cyoffset);
				}
			}
		}
	}
	if (rotatemap && (cxoffset!=0 || cyoffset!=0)) {
		rotatemap=0;
	       display_message(" APPROACHING MAP BOUNDARY!!! ","","TRACK UP OFF",800);
	}

	if (frame>49) frame=0;
	if (gpsOn && warn[0]=='O') {
				if (rotatemap) {
			blitAlphaImageToScreen(0,0,25,25,arrows,228+cxoffset,124+cyoffset);
			blitAlphaImageToScreen((((int)course+22)%360/45)*38,0,38,38,compass,360,18);
		} else
			blitAlphaImageToScreen((((int)course+22)%360/45)*25,0,25,25,arrows,228+cxoffset,124+cyoffset);
	} else
		blitAlphaImageToScreen(frame++/10*23,0,23,23,cursor,228+cxoffset,124+cyoffset);

	blitAlphaImageToScreen(0,(zoom+basezoom-1)*30,123,30,scale,5,PSP_HEIGHT-35);
	draw_zoom_scale(5,20,zoomscale,zoom, maxzoom);
	if (gpsOn){
		int hours=ltime/10000+timezone;
		if (hours<0) hours+=24;
		if (hours>23) hours-=24;
		
		sprintf(printbuf,"LAT:%10.6f LON:%10.6f    GPS:ON  DATA:%3s   %02d:%02d:%02d",  c.lat,c.lon, warn, hours,ltime%10000/100,ltime%100);
	} else
		sprintf(printbuf,"LAT:%10.6f LON:%10.6f  GPS:OFF                      ", c.lat,c.lon);
	draw_string (printbuf,FONT02, 0,0);
	int y_offset=8;
	if (gpsOn) {
		if (gpsout!=NULL)
			sprintf(printbuf,"SATELLITES:%2d    FIX QUALITY:%2d  GPS REC ON!      ", satellites, quality);
		else
			sprintf(printbuf,"SATELLITES:%2d    FIX QUALITY:%2d                   ", satellites, quality);
		draw_string (printbuf,FONT02, 0,8);
		draw_big_string("     ",400,8);
		draw_big_string(" SPD:",400,24);
		draw_big_string("     ",400,40);
		sprintf(printbuf," %4.1f",speed*speedfix);
		draw_big_string(printbuf,400,56);
		draw_big_string("     ",400,72);
		draw_big_string("     ",400,88);
		draw_big_string(" DIR:",400,104);
		draw_big_string("     ",400,120);
		if (warn[0]=='B')
			sprintf(printbuf,"  -- ");
		else
			sprintf(printbuf,"  %2s ",heading[((int)course+22)%360/45]);
		draw_big_string(printbuf,400,136);
		draw_big_string("     ",400,152);
		draw_big_string("     ",400,168);
		draw_big_string(" ALT:",400,184);
		draw_big_string("     ",400,200);
		if (warn[0]=='B')
			sprintf(printbuf,"  -- ");
		else
			sprintf(printbuf," %4.1f",altitude*altfix);
		draw_big_string(printbuf,400,216);
		draw_big_string("     ",400,232);
		draw_big_string("     ",400,248);
		draw_big_string("     ",400,264);
	}

		
	if (strlen(attrbuf1)>0) {
		draw_string (attrbuf1,FONT02, 0,256);
		draw_string (attrbuf2,FONT02, 0,264);
	}
		
	flipScreen();
	if (gpsOn) {
		if (mx<1 && my<1) {
			mx=mapx;
			my=mapy;

		}
		frameDelta=frameTick;
		sceRtcGetCurrentTick(&frameTick);
		frameDelta=frameTick-frameDelta;
		mx+=deltax*frameDelta/deltatime;
		my+=deltay*frameDelta/deltatime;
	
		deltax*=0.95;
		deltay*=0.95;
		mapx=(int)mx;
		mapy=(int)my;

		if (fakefeed)
			getGPSMessageFromFile(); 
		else
			getGPSMessage(); 
	}
	sceKernelDelayThread(2);
	}
	if (gpsout!=NULL) {
		fclose(gpsout);
		gpsout=NULL;
	}
	beep();
	zm++;
	idle=1;
	for (int z=1; z<TILE_NUM; z*=2)
		unload_group(z,zipfile);
        imglist_garbagecollect();
	sceKernelDelayThread(500000);
	if (datatype==1)
		closezipfile();
	if (fp!=NULL)
		fclose(fp);

	}

	cleanup_output();
        sceKernelExitGame();
	return 0;

}



