#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <libgen.h>


#define INDENT "    "





// creates a grid of POIs, using a given offset
void create_POIs(double baseLat, double baseLon, int num_rows, int nodes_per_row)
{
     
    int id, node, phase;

    int nodes=num_rows*nodes_per_row;

    double x_offset = 0.002;    
    double y_offset = 0.004;
    
     // calculate the bounding box, the coordinates of which must be a multiple of 4096 Garmin units
     double botLeft[2], topRight[2]; // [0] = latitude, [1] = longitude
     
     // bottom left
     int garminTemp[2];
     garminTemp[0]=get_Garmin(baseLat);
     garminTemp[1]=get_Garmin(baseLon);

     //printf("%d,\t%d\n",garminTemp[0],garminTemp[1]);
     
     botLeft[0]=garminTemp[0]/46603.0;
     botLeft[1]=garminTemp[1]/46603.0;
     
     printf("Bottom L bounding box latitude=%f\tlongitude=%f\n",botLeft[0],botLeft[1]);
     
     // top Right
     garminTemp[0]=get_Garmin(baseLat+(y_offset*num_rows)+y_offset)+4096;
     garminTemp[1]=get_Garmin(baseLon+(x_offset*nodes_per_row)+x_offset)+4096;
     topRight[0]=garminTemp[0]/46603.0;
     topRight[1]=garminTemp[1]/46603.0;
     printf("Top R bounding box latitude=%f\tlongitude=%f\n",topRight[0], topRight[1]);   

    
    FILE *fp=fopen("points.osm", "wb");
    fprintf(fp, "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='osm_generate'>\n");
    //fprintf(fp, "<bounds minlat='%f' minlon='%f' maxlat='%f' maxlon='%f' origin='OSM_Generate' />\n", botLeft[0], botLeft[1], topRight[0], topRight[1]);
    fprintf(fp, "<bounds minlat='51.37248675' minlon='0.043945669' maxlat='51.50432376' maxlon='0.263674' origin='OSM_Generate' />\n", botLeft[0], botLeft[1], topRight[0], topRight[1]);
    
    for (phase = 0; phase < 3; phase++) {
        for (node=0; node < num_rows; node++) {

            int node_offset = 1 + node * nodes_per_row;
            time_t t = 31 * 365 * 24 * 60 * 60 + node;
            struct tm *ts = gmtime(&t);
            
            switch(phase) {
                case 0: { // Nodes, place in a grid like pattern

                    //  <node id='17233948' timestamp='2007-08-04 22:24:51' visible='true' lat='45.4052394197691' lon='-75.6987485112884' />
                    for (id = 0; id < nodes_per_row; id++) {
                        fprintf(fp, INDENT "<node id='-%d' timestamp='%d-%d-%d %d:%d:%d' visible='true' lat='%.8f' lon='%.8f'>\n",
                               node_offset + id, ts->tm_year + 1900, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec,
                               baseLat+y_offset*node, baseLon+x_offset*id);
                        fprintf(fp, INDENT "  <tag k='point' v='p%04d' />\n",id+1+(nodes_per_row*node));
                        fprintf(fp, INDENT "</node>\n",node);
                    }
                }
                break;

            }
        }
        fprintf(fp, "</osm>\n");
        fclose(fp);
    }
}

// creates a grid of polygons
void createPolys(double baseLat, double baseLon, int num_rows, int num_polysPerRow) {
     // first create all the node vertices, then create ways between the nodes
     
    
     int polys=num_rows*num_polysPerRow;
    
     double y_offset = 0.003;    // the distance between polygons on the y axis
     double x_offset = 0.003;    // the distance between polygons on the x axis
     double edge     = 0.002;    // the length of a polygon edge
     
     
     // calculate the bounding box, the coordinates of which must be a multiple of 4096 Garmin units
     double botLeft[2], topRight[2]; // [0] = latitude, [1] = longitude
     
     // bottom left
     int garminTemp[2];
     garminTemp[0]=get_Garmin(baseLat);
     garminTemp[1]=get_Garmin(baseLon);

     //printf("%d,\t%d\n",garminTemp[0],garminTemp[1]);
     
     botLeft[0]=garminTemp[0]/46603.0;
     botLeft[1]=garminTemp[1]/46603.0;
     
     printf("Bottom L bounding box latitude=%f\tlongitude=%f\n",botLeft[0],botLeft[1]);
     
     // top Right
     garminTemp[0]=get_Garmin(baseLat+((edge+y_offset)*num_rows)+y_offset)+4096;
     garminTemp[1]=get_Garmin(baseLon+((edge+x_offset)*num_polysPerRow)+x_offset)+4096;
     topRight[0]=garminTemp[0]/46603.0;
     topRight[1]=garminTemp[1]/46603.0;
     printf("Top R bounding box latitude=%f\tlongitude=%f\n",topRight[0], topRight[1]);   
       
     FILE *fp=fopen("polygons.osm", "wb");
     fprintf(fp, "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='osm_generate'>\n");
     fprintf(fp, "<bounds minlat='%f' minlon='%f' maxlat='%f' maxlon='%f' origin='OSM_Generate' />\n", botLeft[0], botLeft[1], topRight[0], topRight[1]);
     
     // write the nodes (4 per area)
     int i=0;
     int j=0;
     int counter=1;
     
     
     for (i=0;i<num_rows;i++) { //1

         for (j=0;j<num_polysPerRow;j++) { // 1
           time_t t = 31 * 365 * 24 * 60 * 60 + j;
           struct tm *ts = gmtime(&t);

              fprintf(fp, INDENT "<node id='-%d' timestamp='%d-%d-%d %d:%d:%d' visible='true' lat='%.8f' lon='%.8f' />\n", 
              counter, ts->tm_year + 1900, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec,
              baseLat+(y_offset*i)+(edge*i), baseLon+(x_offset*j)+(edge*j)                 );
              counter++;

              fprintf(fp, INDENT "<node id='-%d' timestamp='%d-%d-%d %d:%d:%d' visible='true' lat='%.8f' lon='%.8f' />\n", 
              counter, ts->tm_year + 1900, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec,
              baseLat+(y_offset*i)+(edge*i), baseLon+(x_offset*j)+(edge*j)+edge            );
              counter++;
              
              fprintf(fp, INDENT "<node id='-%d' timestamp='%d-%d-%d %d:%d:%d' visible='true' lat='%.8f' lon='%.8f' />\n", 
              counter, ts->tm_year + 1900, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec,
              baseLat+(y_offset*i)+(edge*i)+edge, baseLon+(x_offset*j)+(edge*j)            );
              counter++;
              
              fprintf(fp, INDENT "<node id='-%d' timestamp='%d-%d-%d %d:%d:%d' visible='true' lat='%.8f' lon='%.8f' />\n", 
              counter, ts->tm_year + 1900, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec,
              baseLat+(y_offset*i)+(edge*i)+edge, baseLon+(x_offset*j)+(edge*j)+(edge)       );             
              counter++;
         }
     }    
     
     // write the ways
     int nodeRef=1;
     for (i=0;i<num_rows*num_polysPerRow;i++) {
         fprintf(fp, INDENT "<way id='-%d' visible='true'>\n", counter);
         counter++;
         fprintf(fp, INDENT "  <nd ref='-%d' />\n", nodeRef);
         fprintf(fp, INDENT "  <nd ref='-%d' />\n", nodeRef+1);
         fprintf(fp, INDENT "  <nd ref='-%d' />\n", nodeRef+3);
         fprintf(fp, INDENT "  <nd ref='-%d' />\n", nodeRef+2);
         fprintf(fp, INDENT "  <nd ref='-%d' />\n", nodeRef);
         fprintf(fp, INDENT "  <tag k='poly' v='p%04d' />", i+1);
         
         fprintf(fp, INDENT "</way>\n");
         nodeRef+=4;
         }

     
     fprintf(fp, "</osm>\n");
     fclose(fp);

}

// writes a mkgmap style file appropriate to the type of object being created
void writeStyle(int num_objects, int node_start, int outputType)
{       
        

        
        int i=0;
        FILE *fp;
        switch (outputType) {
               
               case 1: //points
                   printf("Print %d nodes, starting at node %d", num_objects, node_start);
                   fp=fopen ("points","wb");
                     for (i=0;i<num_objects;i++) {
                          fprintf(fp,"point=p%04d {name '%#06X'} [%#06X resolution 20]\n", i+1, i+1+node_start, i+1+node_start);                                  
                         }
                     fclose(fp);
               
               case 2: // polygons
                    fp=fopen("polygons","wb");
                     for (i=0;i<num_objects;i++) {
                          fprintf(fp,"poly=p%04d {set name='%#04X'} [%#04X resolution 20]\n", i+1, i+1+node_start, i+1+node_start);
                         }
                         fclose(fp);
               
               case 3: // lines
                    fp=fopen("lines","wb");
                    for (i=0; i<num_objects;i++) {
                         fprintf(fp,"line=l%04d {name='%#06X'} [%#06X resolution 20]\n", i+1, i+1+node_start, i+1+node_start);
                        }
                        fclose(fp);
               }
        


}

// take a latitude and round it to the nearest Garmin latitude
int get_Garmin(double val) {
       
       int conv_factor=46603;
       int overlap_factor=4096;
       
       double inter=(val*conv_factor);
       //printf("Answer step 1=%f\n", inter);
       double modulus=fmod(val*conv_factor,overlap_factor);
       //printf("Answer step 2=%f\n", modulus);
       int answer=inter- modulus;
       //printf("Answer step 2=%d\n", answer); 
             
       return answer;
       
}


// get parameters needed to output a grid of POIs
void get_node_inputs(double baseLat, double baseLon) 
{     
      int outputType=1; // nodes
      
      int num_rows;
      printf("Enter number of rows: \n");
      scanf("%d", &num_rows);
      
      int num_nodesPerRow;
      printf("Enter number POIs per row: \n");
      scanf("%d", &num_nodesPerRow);
      
      int start;
      printf("Enter offset for first node (first node will start with the number you enter +1): \n");
      scanf("%d", &start);
      
      create_POIs(baseLat, baseLon, num_rows,num_nodesPerRow);
      writeStyle(num_rows*num_nodesPerRow, start, outputType);

}

// get parameters needed to output a grid of polygons
void get_poly_inputs(double baseLat, double baseLon)
{
     
     int outputType=2; // polygons
     
     int num_rows;
      printf("Enter number of rows: \n");
      scanf("%d", &num_rows);
      
      int num_polysPerRow;
      printf("Enter number of polygons per row: \n");
      scanf("%d", &num_polysPerRow);         
     
     int start;
     printf("Enter offset for first polygon: \n");
     scanf("%d", &start);
     
     createPolys(baseLat, baseLon, num_rows,num_polysPerRow);
     writeStyle(num_rows*num_polysPerRow,start, outputType);
}


// given a certain Garmin lat/lon: turn it back into coordinates
double reverse_Garmin(int val) {

       double conv_factor=46603.0;
       
       //printf("val=%d\t", val);
       double answer = (val/conv_factor);
       //printf("answer=%f\n", answer);
       return answer;    
}



int main(int argc, char **argv)
{
    char outputType;
    int mode;
    
    double baseLat=51.45826;
    double baseLon=0.17441;

    if (argc<2) {
                printf("No base lat/long entered.\n");
                printf("Using default base latitude=%f\tlongitude=%f\n", baseLat, baseLon);
                }
    else {
         baseLat=strtod(argv[1], NULL);
         baseLon=strtod(argv[2], NULL);
         printf("\nUser input base latitude=%f\tlongitude=%f\n", baseLat, baseLon);
         }
 

    printf("\nPlease enter a choice for output type:\n");
    printf("1: POIs\n2: Polygons\n3: Lines\n4: Quit\n");
    scanf("%d", &mode);

    
    switch (mode) {
           case 1:
                printf("Outputting a grid of nodes...\n");
                get_node_inputs(baseLat, baseLon);
                break;
           
           case 2:
                //get_poly_inputs();
                printf("Outputting a grid of polygons...\n");
                get_poly_inputs(baseLat, baseLon);
                break;
           
           case 3:
                //get_line_inputs();
                printf("Outputting a grid of lines...\n");
                printf("Not yet implemented...quitting\n");
                break;
           
           case 4:
                printf("Quitting...\n");
                break;
                 }
                 

    return 0;
}

