//-*-C++-*-
/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2003 by Leon Pennington
    email                : leon@leonscape.co.uk
**************************************************************************

**************************************************************************
*                                                                        *
*  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.                                   *
*                                                                        *
**************************************************************************/

#ifndef PMHEIGHTFIELDROAM_H
#define PMHEIGHTFIELDROAM_H

#include <cmath>
#include <qstring.h>
#include <qimage.h>
#include <qcolor.h>

/**
 * ROAM display class for @ref PMHeightField
 */
class PMHeightFieldROAM
{
   unsigned short m_size;
   unsigned m_numPoints;
   unsigned m_usedPoints;
   unsigned m_numLines;
   bool m_fail;

   int m_maxLevel;
   unsigned short m_distance;
   unsigned short m_waterLevel;

   /**
    * Point Structure holds most of the info
    */
   struct pointStructure
   {
      unsigned short hgt;
      pointStructure* lines[ 8 ];
      unsigned pos;
      bool used;
   };
   /**
    * The points array
    */
   pointStructure* m_pPoints;

   /**
    * The Triangle Node structure used in the ROAM algorithm
    */
   struct triNodeStructure
   {
      triNodeStructure* lchd;
      triNodeStructure* rchd;
      triNodeStructure* base;
      triNodeStructure* lnbr;
      triNodeStructure* rnbr;
      int vari;
      bool split;
   };
   /**
    * Tree Root node
    */
   triNodeStructure* m_pTree;
   /**
    * Next available node
    */
   triNodeStructure* m_pNextNode;

   /**
    * Loads and formats the image to the correct size, creates the points
    * array. Then fills the points array with the heights and zero's the
    * rest of the info
    * @param filename The name of the file to load
    * @return true if succesful false it it fails
    */
    bool mapData( const QString fileName );

   /**
    * Sets the Maximum Level of the tree.
    */
   void calcLevel( );

   /**
    * Generates the Variance for each node.
    * @param current The current node
    * @param x1-y1 The position of the first corner
    * @param x2-y2 The position of the second corner
    * @param x3-y3 The position of the third corner
    * @param level The current level within a tree
    */
   void varNode( triNodeStructure* current,
                 int x1, int y1,
                 int x2, int y2,
                 int x3, int y3,
                 int level );

   /**
    * Generates the Split for the Tree.
    * @param current The current node
    * @param level The current level within a tree
    */
   void sptNode( triNodeStructure* current, int level );

   /**
    * Request the splitting of a node, checks too see if it can be
    * Split or if its base neighbour requires splitting.
    * @param current node to split
    */
   void split( triNodeStructure* current );

   /**
    * Counts up the lines and points in a model, and sets the points
    * structure ready for returning
    * @param current The current node
    * @param x1-y1 The position of the first corner
    * @param x2-y2 The position of the second corner
    * @param x3-y3 The position of the third corner
    */
   void pntNode( triNodeStructure* current,
                 int x1, int y1,
                 int x2, int y2,
                 int x3, int y3 );

   /**
    * Adds a line makes sure that this line does not already exist
    * @param pts1 The start point in the line
    * @param pts2 The end point of the line
    */
   void addLine( pointStructure* pts1, pointStructure* pts2 );

   /**
    * Clears some of the points data in the event of a detail change
    */
   void clearPoints( );

public:
   /**
    * Constructor for class
    * @param source Source map for the model
    * @param waterLevel The water level for the object
    * @param detail The detail level
    */
   PMHeightFieldROAM( const QString fileName,
                      double waterLevel = 0.0,
                      unsigned short detail = 1 );

   /**
    * Class Destructor relases all the momory
    */
   ~PMHeightFieldROAM( );

   /**
    * Returns true if there has been a problem
    */
   bool isFailed( ) { return m_fail; }

   /**
    * Sets the level of detail for the model, and regenrates it based on this
    * detail level
    * @param detail The new level of detail
    */
   void setLOD( unsigned short detail );

   /**
    * Return The Current Detail Level
    */
   unsigned char LOD( ) const { return (257 - (m_distance / 256) ); }

   /**
    * Returns the current water level
    */
   double waterLevel( ) const { return ( (double)m_waterLevel / 65535.0); }

   /**
    * Return The size of the map in either direction
    */
   unsigned short size( ) const { return m_size; }

   /**
    * Return The total number of points in the model
    */
   unsigned numPoints( ) const { return m_numPoints; }

   /**
    * Return The number of used points
    */
   unsigned usedPoints( ) const { return m_usedPoints; }

   /**
    * Return The number of lines
    */
   unsigned numLines( ) const { return m_numLines; }

   /**
    * Returns a specified points height.
    * @param x the postion of the point in X
    * @param y the postion of the point in Y
    * @return the height of the point
    */
   unsigned short height( unsigned short x, unsigned short y ) const
         { return m_pPoints[ x + ( y * m_size ) ].hgt; }

   /**
    * Determines if the point is used
    * @param x The position of the point on the x axis.
    * @param y The position of the point on the y axis.
    * @return true if the point is used else false
    */
   bool usedPoint( unsigned short x, unsigned short y ) const
         { return m_pPoints[ x + ( y * m_size ) ].used; }

   /*!
    * Gets the used postion of a point
    * @param x The position of the point on the x axis.
    * @param y The position of the point on the y axis.
    * @return the used position
    */
   unsigned posPoint( unsigned short x, unsigned short y ) const
         { return m_pPoints[ x + ( y * m_size ) ].pos; }

   /**
    * Returns the used position of a point at the end point of a line.
    * @param x The position of the start point on the x axis.
    * @param y The position of the start point on the y axis.
    * @param line The Line Index
    * @return The used positon of the end point
    */
   unsigned endPoint( unsigned short x, unsigned short y, int line ) const
         { return m_pPoints[ x + ( y * m_size ) ].lines[ line ]->pos; }

   /**
    * Returns whether this line exists
    * @param x The position of the start point on the x axis.
    * @param y The position of the start point on the y axis.
    * @param line The Line Index
    * @return Whether the line exists
    */
   bool lineExist( unsigned short x, unsigned short y, int line ) const;
};

#endif
