/****************************************************************************
 * 
 *  MODULE:	r.terraflow
 *
 *  COPYRIGHT (C) 2007 Laura Toma
 *   
 *  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.
 *
 *****************************************************************************/

#ifndef _direction_H
#define _direction_H


#include <stdio.h>

#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
#include <ostream>
#else
#include <ostream.h>
#endif

using namespace std;

#include <grass/iostream/ami.h>

#include "types.h"
#include "genericWindow.h"
#include "nodata.h"



/***************************************************************/
/***************************************************************/
class directionWindow: public genericWindow<bool> {

public:
  int numdir;

  /***************************************************************/
  /* Modifies the window by setting to 1 only the neighbors to which
     direction point. This is the inverse function of encodeDirection().
     
     direction:   
     32 64 128  
     16 *   1   
     8  4   2 */
  directionWindow(direction_type dir)
    : genericWindow<bool>() {

    /* first set everything to 0 */
    numdir = 0;
    int i;
    for (i=0; i<9; i++) {
      set(i, false);
    }

    if (dir == 0 || dir == DIRECTION_UNDEF) {
      return;
    }
    assert(dir > 0 && dir < 256);
    if (dir & 1) {
      set(5, true); numdir++;
    }
    if (dir & 2) {
      set(8, true); numdir++;
    }
    if (dir & 4) {
      set(7, true); numdir++;
    }
    if (dir & 8) {
      set(6, true); numdir++;
    }
    if (dir & 16) {
      set(3, true); numdir++;
    }
    if (dir & 32) {
      set(0, true); numdir++;
    }
    if (dir & 64) {
      set(1, true); numdir++;
    }
    if (dir & 128) {
      set(2, true); numdir++;
    }
  }

  /***************************************************************/
  /* Check direction consistency. */
  void checkDirection(short di, short dj, int skipit,
		      elevation_type el, elevation_type elneighb) const {
#ifndef NDEBUG 
    if (skipit == 1) {
      assert(get(di,dj) ==  false);
    } else {
      if ((el > elneighb) && !is_nodata(elneighb) && !is_nodata(el)) {
	assert(get(di,dj) ==  true);
      }
    }
#endif   
  }
  
  
  /***************************************************************/
  /* Correct direction (di,dj). If direction points to invalid
     neighbor which must be skipped, set it to 0; if direction does
     not point to valid downslope neighbor, set it to 1. */
  void correctDirection(short di, short dj, int skipit, 
			dimension_type i, dimension_type j,
			elevation_type elev_crt, direction_type dir,
			elevation_type elev_neighb) {
    
    if (skipit && get(di,dj) == true) {
      cout << "WARNING:  at (" 
	   << i << "," << j << " , h=" << elev_crt << ", dir=" << dir << ")"
	   << "direction points to non-valid neighbor ("
	   <<  i + di << ","
	   <<  j + dj << ", h="
	   << elev_crt - elev_neighb
	   << ")\n";
      set(di,dj, false); /* correct it */
    }
    if (!skipit && elev_crt > elev_neighb &&  !is_nodata(elev_neighb)
	&& get(di,dj) == 0) {
      set(di,dj, true); /* correct it */
    }
  }
};


direction_type encodeDirection(const genericWindow<elevation_type>& elevwin,
			       const dimension_type nrows, 
			       const dimension_type ncols,
			       dimension_type row, dimension_type col);

direction_type encodeDirectionMFD(const genericWindow<elevation_type>& elevwin,
			       const dimension_type nrows, 
			       const dimension_type ncols,
			       dimension_type row, dimension_type col);

direction_type encodeDirectionSFD(const genericWindow<elevation_type>& elevwin,
			       const dimension_type nrows, 
			       const dimension_type ncols,
			       dimension_type row, dimension_type col);

direction_type findDominant(direction_type dir);
char directionSymbol(direction_type dir);


#endif

