int main(int argc, char ** argv)
{
  Projection * to, * from;              //projections
  PmeshLib::ProjectionMesh pmesh; 
  std::string infile, inparam, outparam, 
    outfile("pout.txt");                //input/output files
  bool usepmesh = false;                //use the pmesh
  int pmeshsize = 4;                    //default pmesh size
  int myopt;                            //for options
  int interpolator = 7;                 //which interpolator for pmesh
  long int counter;
  double x, y;                          //for points
  long int numpoints;                   //numberofpoints
  std::ifstream in;
  std::ofstream out;
  std::ofstream compare;
  double min_x, min_y, max_x, max_y;
  double tempx, tempy;
  std::vector<double> xarr, yarr;
  double errorx, errory;
  std::string comparefile("compare.txt");

  try
    {
      //first parse the command args
       //parse the arguments
     while (( myopt = getopt(argc, argv, "c:n:l:p:f:i:o:?")) != -1)
       switch(myopt)
         {
         case 'c':
           {
             if (optarg)
               comparefile = optarg;
             break;
           }
         case 'n':
           {
             if (optarg)
               pmeshsize = atoi(optarg);
             break;
           }
         case 'p':
           {
             if (optarg)
               {
                 interpolator = atoi(optarg);
                 usepmesh = true;
               }
             break;
           }
         case 'f': //input file switch
           {
             if (optarg) // only change if they entered something
               infile = std::string(optarg);
             break;
           }
         case 'i': //parameter file for input
           {
             if (optarg)
               inparam = std::string(optarg);
             break;
           }
         case 'o':
           {
             if (optarg)
               outparam = std::string(optarg);
             break;
           }
         case '?':
         default: // help
           {
             std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
             std::cout << "where options are: " << std::endl;
             std::cout << "   -f input file to reproject" << std::endl;
             std::cout << "   -i input parameter file" << std::endl;
             std::cout << "   -? this help screen"   
                       << "(Which you obviously already knew about)"
                       << std::endl;
             return 0;;
           }
         }

     from = SetProjection(inparam);
     to = SetProjection(outparam);
     in.open(infile.c_str());
     
     in >> numpoints;
     
     xarr.resize(numpoints);
     yarr.resize(numpoints);
     
     for (counter = 0; counter < numpoints; counter++)
       in >> xarr[counter] >> yarr[counter];
     
     in.close();
     
     if (usepmesh)
       {
         // getMinMax(xarr, min_x, max_x);
         // getMinMax(yarr, min_y, max_y);
         min_x = 314850.0;
         max_x = min_x + 6224;
         max_y =  4291485.0;
         min_y = max_y - 7673.0;

          //setup the foward mesh
         pmesh.setSourceMeshBounds(min_x, min_y, max_x, max_y);
         pmesh.setMeshSize(pmeshsize,pmeshsize); 
    
         pmesh.setInterpolator(interpolator);
         //now project all the points onto the mesh
         pmesh.calculateMesh((*from), (*to));
     
       }
     
     out.open(outfile.c_str());
     compare.open(comparefile.c_str());

     for (counter = 0; counter < numpoints; counter++)
       {
         if (usepmesh)
           {
             tempx = xarr[counter];
             tempy = yarr[counter];
             from->projectToGeo(xarr[counter], yarr[counter], tempy, 
                                tempx);
             to->projectFromGeo(tempy, tempx, tempx, tempy);

             x = xarr[counter];
             y = yarr[counter];
             pmesh.projectPoint(x, y);
             
             errorx = tempx - x;
             errory = tempy - y;

             if (to->getUnit() == US_FEET)
               {
                 
                 errorx = errorx/3.28083988; //convert to meters
                 errory = errory/3.28083988; //convert to meters
               }
                 if (errorx < 0)
                   errorx = -errorx;
                 if (errory < 0)
                   errory = -errory;
                 
                 compare << tempx << "\t" << tempy
                         << "\t" << x << "\t" << y
                         << "\t" << errorx 
                         << "\t" << errory << "\t";
                 errorx = sqrt(errorx*errorx + errory*errory);
                 compare << errorx << std::endl; //distance
           }
         else
           {
             from->projectToGeo(xarr[counter], yarr[counter], y, x);
             to->projectFromGeo(y, x, x, y);
           }
         
         out << x << " " << y << std::endl;
       }
     out.close();
     compare.close();
     delete from;
     delete to;
     return 0;
    }
  catch(...)
    {
      delete from;
      delete to;
      return 0;
    }
}
int main(int argc, char **argv)
{
 ProjIOLib::ProjectionReader reader;
 ProjIOLib::ProjectionWriter writer;
 Projection * fromprojection, * toprojection;
 USGSImageLib::ImageIFile * infile;
 USGSImageLib::GeoTIFFImageOFile* out = NULL; //the output file  
 USGSImageLib::CacheManager* cache = NULL;    //cache
 USGSImageLib::GeoTIFFImageIFile* ingeo;
 USGSImageLib::DOQImageIFile * indoq;
 PmeshLib::ProjectionMesh * pmesh = NULL;     //projection mesh
 int myopt;
 std::string filename(" ");
 std::string parameterfile(" ");
 long int xcounter, ycounter;                 //counter for loops
 long int height, width, newheight, newwidth; //image metrics
 double x, y;                                 //projection constants
 long int _x, _y;                             // for inputs
 int pmeshsize = 4;                           //pmeshsize default is 10
 unsigned char*  scanline = NULL;             //output scanline
 unsigned char*  inscanline = NULL;           // for the input scanlines
 std::string datumtype, units;                //strings for checks
 double left, right, bottom, top;             //projected extremes
 double min_x, min_y, max_x, max_y;           //unprojected extremes
 int photo, spp, bps;                         //image type
 float xscale = 0.0;                          //scale
 float yscale = 0.0; 
 std::string outfile("out.tif");              //output filename
 int interpolator = 0;

 // Arrays used to find the minimum and maximum values
 std::vector<double> xarr, yarr;
 double newscale;
 double oldscale;
 double scale[2] = {0};
 double tp[6] = {0}; //geotiff tie points
 double res[2] = {0}; //geotiff resoultion
 std::string logname("time.out");
 std::string name;
;
 double tempx, tempy;
 try
   {
     //parse the arguments
     while (( myopt = getopt(argc, argv, "l:n:p:f:i:?")) != -1)
       switch(myopt)
         {
         case 'l':
           {
             if (optarg)
               {
                 logname = optarg;
               }
           }
         case 'n':
           {
             if (optarg)
               {
                 pmeshsize = atoi(optarg);
                 
               }
           }
           break;
         case 'p':
           {
             if (optarg)
               {
                interpolator = atoi(optarg);
                 
               }
             break;
           }
         case 'f': //input file switch
           {
             if (optarg) // only change if they entered something
               filename = std::string(optarg);
             break;
           }
         case 'i': //parameter file for output
           {
             if (optarg)
               parameterfile = std::string(optarg);
             break;
           }
         case '?':
         default: // help
           {
             std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
             std::cout << "where options are: " << std::endl;
             std::cout << "   -f input file to reproject" << std::endl;
             std::cout << "   -i input parameter file" << std::endl;
             std::cout << "   -? this help screen"   
                       << "(Which you obviously already knew about)"
                       << std::endl;
             return 0;;
           }
         }
     
     if ((filename == std::string(" ")) || (parameterfile == std::string(" ")))
       {
         std::cout << "You must enter a input file and a parameter file" 
                   << " use -? for options" << std::endl;
         return 0;
       }

     //start the time
     name = std::string("date > ") + logname; 
     system(name.c_str());

     //try to open the file as a doq
      //try to open the file
    if (!(infile = new(std::nothrow)USGSImageLib::DOQImageIFile(filename)))
      throw std::bad_alloc();
        
    if (!infile->good())
      {
        delete infile;
        
        if (!(infile = new(std::nothrow)
              USGSImageLib::GeoTIFFImageIFile(filename)))
          throw std::bad_alloc();
            
        if (!infile->good())
          {
            std::cout << "Invalid file type" << std::endl;
            return 0;
          }
        
        fromprojection = reader.createProjection
          (dynamic_cast<USGSImageLib::GeoTIFFImageIFile*>(infile));
        
        if (!fromprojection)
          {
            std::cout << "Unable to open a projection for input file"
                      << std::endl;
            return 0;
          }
        
        //get the scale and such
        ingeo = dynamic_cast<USGSImageLib::GeoTIFFImageIFile *>(infile);
        ingeo->getPixelScale(scale);
        if (scale[0] != scale[1])
          {
            std::cout << "scale different in x and y directions" << std::endl;
            throw;
          }
        oldscale=scale[0];
        ingeo->getTiePoints(tp, 6);
        min_x = tp[3];
        max_y = tp[4];
        
      }
    else
      {
        fromprojection = reader.createProjection
          (dynamic_cast<USGSImageLib::DOQImageIFile*>(infile));
        
        if (!fromprojection)
          {
            std::cout << "Unable to open a projection for input file"
                      << std::endl;
            return 0;
          }

        indoq = dynamic_cast<USGSImageLib::DOQImageIFile * > (infile);
        
        indoq->getHorizontalResolution(xscale);
        oldscale = xscale;
        indoq->getXOrigin(min_x);
        indoq->getYOrigin(max_y);
       
      }

    //get some image metrics
    infile->getHeight(height);
    infile->getWidth(width);
    infile->getSamplesPerPixel(spp);
    infile->getBitsPerSample(bps);
    infile->getPhotometric(photo);    

    //create the cache for the file
    if (!(cache = new(std::nothrow) USGSImageLib::CacheManager(infile, 300, 
                                                               true)))
      throw std::bad_alloc();
    if (!cache->good())
      {
        std::cerr << "Doh!  Cache is bad!" << std::endl;
        throw USGSImageLib::ImageException(IMAGE_FILE_OPEN_ERR);
      }


    max_x = min_x + oldscale * width;
    min_y = max_y - oldscale * height;
    
    //TODO create the to projection here
    toprojection = SetProjection(parameterfile);
    
    if (!toprojection)
      throw std::bad_alloc();

    //get the new scale
    newscale = GetConvertedScale(fromprojection, toprojection, oldscale);
    
    //setup Pmesh
    if (interpolator != 0)
      {
        if (!(pmesh = new (std::nothrow) PmeshLib::ProjectionMesh()))
          throw std::bad_alloc();
        
         //setup the foward mesh
        pmesh->setSourceMeshBounds(min_x, min_y, max_x, max_y);
        pmesh->setMeshSize(pmeshsize,pmeshsize); 
        pmesh->setInterpolator(interpolator);
        //now project all the points onto the mesh
        pmesh->calculateMesh((*fromprojection), (*toprojection));
      }

    
    //make room in the vector for the edges
    xarr.resize(2*(width+height));
    yarr.resize(2*(height + width));
    
    //now loop through and project the edges
    for (xcounter = 0; xcounter < width; xcounter++)
    {
      //reproject the top line
      tempx = min_x + oldscale*xcounter;
      tempy = max_y;
      if (pmesh)
        pmesh->projectPoint(tempx, tempy);
      else
        {
          fromprojection->projectToGeo(tempx, tempy, tempy, tempx);
          toprojection->projectFromGeo(tempy, tempx, tempx, tempy);
        }
      xarr[xcounter] = tempx;
      yarr[xcounter] = tempy;

      //reproject the bottom line
      tempx = min_x + oldscale*xcounter;
      tempy = min_y;
      if (pmesh)
        pmesh->projectPoint(tempx, tempy);
      else
        {
          fromprojection->projectToGeo(tempx, tempy, tempy, tempx);
          toprojection->projectFromGeo(tempy, tempx, tempx, tempy);
        }
      xarr[xcounter + width] = tempx;
      yarr[xcounter + width] = tempy;
    }
    
    //now do the height
    for (ycounter = 0; ycounter < height; ycounter++)
    {
      //reproject the left line
      tempy = max_y - oldscale * ycounter;
      tempx = min_x;
      if (pmesh)
        pmesh->projectPoint(tempx, tempy);
      else
        {
          fromprojection->projectToGeo(tempx, tempy, tempy, tempx);
          toprojection->projectFromGeo(tempy, tempx, tempx, tempy);
        }
      xarr[ycounter + 2*width] = tempx;
      yarr[ycounter + 2*width] = tempy;
      
      //reproject the right line
      tempy = max_y - oldscale * ycounter;
      tempx = max_x;
      if (pmesh)
        pmesh->projectPoint(tempx, tempy);
      else
        {
          fromprojection->projectToGeo(tempx, tempy, tempy, tempx);
          toprojection->projectFromGeo(tempy, tempx, tempx, tempy);
        }
      xarr[ycounter + 2*width + height] = tempx;
      yarr[ycounter + 2*width + height] = tempy;
    }


    //get the min and max of the coordinates
    getMinMax(xarr, left, right);
    getMinMax(yarr, bottom, top);
    if (pmesh)
      {
        //now setup the reverse projection mesh
        pmesh->setSourceMeshBounds(left, bottom, right, top);
        pmesh->setMeshSize(pmeshsize,pmeshsize); 
        pmesh->setInterpolator(interpolator);
        //now project all the points onto the mesh
        pmesh->calculateMesh((*toprojection), (*fromprojection));
      }
    
    //get the new pixel width and height
    newwidth  = static_cast<long int>((right - left)/(newscale) + 0.5);
    newheight = static_cast<long int>((top - bottom)/(newscale) + 0.5);
    
    
    //create the scanline
    if (!(scanline = new(std::nothrow)unsigned char[newwidth]))
      throw std::bad_alloc();
    
    if (!(inscanline = new(std::nothrow) unsigned char[newwidth]))
      throw std::bad_alloc();

    //Create the output file
    out = writer.createGeoTIFF(toprojection, outfile, newwidth, 
                               newheight, photo);
    
    if (!out)
      {
        std::cout << "Unable to open the output file" << std::endl;
        return 0;
      }
      
    tp[3]=left;
    tp[4]=top;
    res[0] = newscale;
    res[1] = newscale;
    
    out->setSamplesPerPixel(spp);
    out->setBitsPerSample(bps);
    out->setTiePoints(tp, 6);
    out->setPixelScale(res);
    out->setPlanarConfig(1);

    //now start addding pixels
    for (ycounter =0; ycounter < newheight; ycounter++)
      {
        std::cout << ycounter+1 << " of " << newheight 
                  << " processed" << std::endl;
        
        for (xcounter = 0; xcounter < newwidth; xcounter++)
          {   
            x = left + newscale * xcounter;
            y = top  - newscale * ycounter;
                
            //now get the reverse projected value
            if (pmesh)
              pmesh->projectPoint(x, y);
            else
              {
                toprojection->projectToGeo(x, y, y, x);
                fromprojection->projectFromGeo(y, x, x, y);
              }
            _x = static_cast<long int>((x - min_x) / (oldscale) + 0.5);
            _y = static_cast<long int>((max_y - y) / (oldscale) + 0.5);

            if ((_x >= width) || (_x < 0) || (_y >= height) || (_y < 0))
              {
                scanline[xcounter] = 0;
              }
            else
              {
                cache->getRawScanline(_y, inscanline);
                scanline[xcounter] = inscanline[static_cast<long int>(_x)];
              }
            }
        //write the scanline
        out->putRawScanline(ycounter, scanline);
      }

    //end the done time
    name = std::string("date >> ") + logname;
    system(name.c_str());
    
    delete cache;
    delete toprojection;
    delete [] scanline;
    delete infile;
    return 0;
    
  }
 catch(ProjectionException &e)
 {
   std::cout << "projection exeception thrown" << std::endl;
   delete toprojection;
   delete [] scanline;
   delete infile;
   delete cache;
   return 0;
 }
 catch(USGSImageLib::ImageException &ie)
   {
     std::cout << "image exeception thrown" << std::endl;
     delete toprojection;
     delete [] scanline;
     delete infile;
     delete cache;
     return 0;
   }
 catch(...)
  {
    std::cout << "An error ocurred at inscanline " << y << std::endl
              << "output scanline " << ycounter << std::endl;
    delete toprojection;
    delete [] scanline;
    delete infile;
    delete cache;
    return 0;
  }

}