/*-------------------------------------------------------------------------
  MapMaker.hxx

  Written by Per Liedman, started February 2000.
  Based on a perl-script written by Alexei Novikov (anovikov@heron.itep.ru)

  Copyright (C) 2000 Per Liedman, liedman@home.se

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
---------------------------------------------------------------------------
  CHANGES
  2000-02-26      Created
  2000-02-06      MapMaker now draws in an OpenGL context
---------------------------------------------------------------------------*/

#include <simgear/compiler.h>
#include <simgear/io/sg_binobj.hxx>
#include <plib/sg.h>
#include <vector>
#include <list>
#include <map>
#include STL_STRING

#include "Output.hxx"
#include "Overlays.hxx"
#include "Geodesy.hxx"

SG_USING_STD(vector);
SG_USING_STD(string);

// Utility function that I needed to put somewhere - this probably isn't the best place for it.
// Appends a path separator to a directory path if not present.
// Calling function MUST ENSURE that there is space allocated for the potential strcat.
void NormalisePath(char* dpath);

class MapMaker {
public:
  static const int DO_SHADE        =  1;
  static const int DO_AIRPORTS     =  2;
  static const int DO_NAVAIDS      =  4;
  static const int DO_VERBOSE      =  8;
  static const int DO_SMOOTH_COLOR = 16; 
  static const int DO_NAVAIDS_VOR  = 32;
  static const int DO_NAVAIDS_NDB  = 64;
  static const int DO_NAVAIDS_FIX  =128;
  static const int DO_NAVAIDS_ILS  =256;
  static const int DO_ALL_FEAT     = 1+2+4+8+16+32+64+128+256;

  // CONSTRUCTOR
  MapMaker( char *fg_root = 0, 
	    char *ap_filter = NULL, 
	    int features = DO_SHADE | DO_AIRPORTS | DO_NAVAIDS |
	                   DO_NAVAIDS_VOR | DO_NAVAIDS_NDB | DO_NAVAIDS_FIX | DO_NAVAIDS_ILS,
	    int size = 256, int scale = 100000 );
  ~MapMaker();

  void setFGRoot( char *path );
  void setPalette( char *path );
  inline void setAPFilter( char *filter ) { arp_filter = filter; }
  inline void setFeatures( int features) { this->features = features; }
  inline void setScale( int scale = 100000 ) { scle = scale; }
  inline void setSize( int size = 512 ) { this->size = size; }
  inline void setDeviceSize( int size = 512 ) { this->device_size = size; }
  inline void setLight( sgVec3 light ) { 
    sgCopyVec3( map_light, light ); 
    sgNormaliseVec3( map_light );
  }
  inline void setLight( float x, float y, float z ) {
    sgSetVec3( map_light, x, y, z );
    sgNormaliseVec3( map_light );
  }

  inline char *getFGRoot() { return fg_root; }
  inline char *getAPFilter() { return arp_filter; }
  inline bool getShade() { return ( features & DO_SHADE ) != 0; }
  inline bool getAirports() { return ( features & DO_AIRPORTS ) != 0; }
  inline bool getVerbose() { return ( features & DO_VERBOSE ) != 0; }
  inline bool getNavaids() { return ( features & DO_NAVAIDS ) != 0; }
  inline bool getNavaidsVOR() { return ( features & DO_NAVAIDS_VOR ) != 0; }
  inline bool getNavaidsNDB() { return ( features & DO_NAVAIDS_NDB ) != 0; }
  inline bool getNavaidsFIX() { return ( features & DO_NAVAIDS_FIX ) != 0; }
  inline bool getNavaidsILS() { return ( features & DO_NAVAIDS_ILS ) != 0; }
  inline int getScale() { return scle; }
  inline int getSize() { return size; }
  inline int getDeviceSize() { return device_size; }
  inline float *getLight() { return light_vector; }
  inline int getFeatures() { return features; }

  int createMap(GfxOutput *output, float theta, float alpha, 
		string dirpath, float autoscale = 0.0f);
  int drawOverlays(GfxOutput *output, float theta, float alpha, 
		bool do_square = false, bool flipy = false );

protected:
  /* colours and materials */
  typedef std::map<string, int> StrMap;

  vector<float*> palette;
  StrMap materials;
  void read_materials(char* filename = NULL);
  
  // Elevation limits for colours
  static const int MAX_ELEV_LEVELS = 27;
  int number_elev_levels;
  int elev_height[MAX_ELEV_LEVELS]; 
  int elev_colindex[MAX_ELEV_LEVELS];

  /* member variables for the run */
  char *fg_root, *arp_filter;

  int features;
  int  size, scle, device_size;
  float zoom;          // size / scale

  bool modified, palette_loaded;

  sgVec3 map_light, light_vector;
  GfxOutput *output;

  /* some info variables */
  int polys;

  inline int elev2colour( int elev ) {
    int i;
    for (i = 0; i < number_elev_levels-1 && elev >= elev_height[i]; i++);
  
    return elev_colindex[i];
  }

# define APPROX(Ca,Cb,X,D) ((Cb-Ca)/D*X+Ca)
  inline void elev2colour_smooth( int elev, float color[4] ) {
    int i,j;
    for (i = 0; i < number_elev_levels-1 && elev > elev_height[i]; i++);
    for (j = number_elev_levels-1; j > 0 && elev <= elev_height[j]; j--);
    if(i==j){
      color[0]=palette[elev_colindex[j]][0];
      color[1]=palette[elev_colindex[j]][1];
      color[2]=palette[elev_colindex[j]][2];
      color[3]=palette[elev_colindex[j]][3];
    } else {
      float diver=(float)(elev_height[i] - elev_height[j]);
      float pom=(float)(elev - elev_height[j]);
      color[0]=APPROX(palette[elev_colindex[j]][0],palette[elev_colindex[i]][0],pom,diver);
      color[1]=APPROX(palette[elev_colindex[j]][1],palette[elev_colindex[i]][1],pom,diver);
      color[2]=APPROX(palette[elev_colindex[j]][2],palette[elev_colindex[i]][2],pom,diver);
      color[3]=APPROX(palette[elev_colindex[j]][3],palette[elev_colindex[i]][3],pom,diver);    
    }
    return;
  }

/****************************************************************************/
  inline void level_triangle( sgVec3 u, sgVec3 v, sgVec3 un, sgVec3 vn, int k, 
			      float *x, float *y, sgVec3 n, int *h ) {
    float min = (u[2] < v[2]) ? u[2] : v[2];

    int j = elev2colour( (int)min )-1;
    *h = j + k - 1;
    float t = ((float)elev_height[*h] - u[2]) / (v[2] - u[2]);
    *x = u[0] + t * (v[0] - u[0]);
    *y = u[1] + t * (v[1] - u[1]);
    // n = un + t*(vn-un)
    sgSubVec3(n, vn, un);
    sgScaleVec3(n, t);
    sgAddVec3(n, un);
  }

  inline float shade( sgVec3 *t ) {
    sgVec3 u, v, n;
  
    sgSubVec3( u, t[0], t[1] );
    sgSubVec3( v, t[0], t[2] );

    sgVectorProductVec3( n, u, v );
    sgNormaliseVec3( n );

    return fabs(sgScalarProductVec3( n, light_vector ));
  }

  void sub_trifan( const int_list &indices, vector<float*> &v, 
		   vector<float*> &n );
  void draw_trifan( const int_list &indices, 
		    vector<float*> &v, vector <float*> &n, int col );
  void draw_tri( const int_list &indices, 
		 vector<float*> &v, vector <float*> &n, int col );
  void draw_tristrip( const int_list &indices, 
		      vector<float*> &v, vector <float*> &n, int col );
		    
  int process_directory( char *path, size_t plen, int lat, int lon, sgVec3 xyz );
  int process_binary_file( char *tile_name, sgVec3 xyz );
  int process_ascii_file( char *tile_name, sgVec3 xyz );
  
/*****************************************************************************/

  static const float simple_normals[][3];
};

