// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* 
// � University Corporation for Atmospheric Research (UCAR) 2009-2010. 
// All rights reserved.  The Government's right to use this data and/or 
// software (the "Work") is restricted, per the terms of Cooperative 
// Agreement (ATM (AGS)-0753581 10/1/08) between UCAR and the National 
// Science Foundation, to a "nonexclusive, nontransferable, irrevocable, 
// royalty-free license to exercise or have exercised for or on behalf of 
// the U.S. throughout the world all the exclusive rights provided by 
// copyrights.  Such license, however, does not include the right to sell 
// copies or phonorecords of the copyrighted works to the public."   The 
// Work is provided "AS IS" and without warranty of any kind.  UCAR 
// EXPRESSLY DISCLAIMS ALL OTHER WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
// ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
// PURPOSE.  
//  
// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* 
#include <toolsa/copyright.h>
/**
 * @file ObarComputeMgr.cc
 */

#include "ObarComputeMgr.hh"
#include <Epoch/TileRange.hh>
#include <ConvWxIO/InterfaceParm.hh>
#include <ConvWxIO/InterfaceIO.hh>
#include <ConvWxIO/Trigger.hh>
#include <ConvWx/Grid.hh>
#include <toolsa/LogStream.hh>

#include <vector>
using std::vector;

//----------------------------------------------------------------
ObarComputeMgr::
ObarComputeMgr(const ParmsObarComputeIO &parms, void tidyAndExit(int)) :
  _parms(parms),
  _obsTime(0),
  _spdb(parms._obarSpdb, parms._inputField, parms._tileInfo,
	parms._obsThreshold),
  _obarInfo((int)parms._obsThreshold.size(), parms._tileInfo.numTiles())
{
  // Standard initialization
  if (!InterfaceParm::driverInit(_parms._main, tidyAndExit))
  {
    tidyAndExit(convWx::BAD_EXIT);
  }
}

//----------------------------------------------------------------
ObarComputeMgr::~ObarComputeMgr()
{
}

//----------------------------------------------------------------
void ObarComputeMgr::run(void)
{
  // Infinite loop of triggering new data 
  time_t obsTime;

  while (Trigger::trigger(_parms._main, obsTime))
  {
    _process(obsTime);
  }
}

//----------------------------------------------------------------
void ObarComputeMgr::_process(const time_t &obsTime)
{
  Grid obsGrid;
  bool hasObs = InterfaceIO::loadObs(obsTime, _parms._proj,
				     _parms._obs.pUrl,
				     _parms._obs.pField,
				     _parms._obs.pRemap,
				     obsGrid);
  if (!hasObs)
  {
    LOG(ERROR) << "Reading Obs data, no processing";
    return;
  }

  _obsTime = obsTime;
  _spdb.updateTime(_obsTime);
  _processTiles(obsGrid);
  _obarInfo.update(_spdb);
  //_spdb.print();
  _spdb.write(obsTime);
}

//----------------------------------------------------------------
void ObarComputeMgr::_processTiles(const Grid &obsGrid)
{

  // for each obar threshold
  for (size_t i=0; i<_parms._obsThreshold.size(); ++i)
  {
    // do the mothertile first
    int motherIndex = TileInfo::motherTileIndex();
    TileObarInfo info = _setupAndRunAlg(obsGrid, motherIndex,
					i, true);
    _obarInfo[i][motherIndex] = info;
    if (!_motherFail)
    {
      _motherSet = true;
      _motherInfo = info;
    }

    // now do all the other tiles
    for (int tileIndex=0; tileIndex<_parms._tileInfo.numTiles(); ++tileIndex)
    {
      if (tileIndex != motherIndex)
      {
	info = _setupAndRunAlg(obsGrid, tileIndex,
			       i, false);
	_obarInfo[i][tileIndex] = info;
      }
    }
  }
}

//----------------------------------------------------------------
TileObarInfo ObarComputeMgr::_setupAndRunAlg(const Grid &obsGrid,
					     int tileIndex,
					     int threshIndex,
					     bool isMotherTile)
{
  int belowTile;
  TileObarInfo ret;
  if (_parms._tileInfo.outOfBoundsY(tileIndex, belowTile))
  {
    // try to use the tile below
    if (!_obarInfo[threshIndex].useTileBelow(tileIndex, belowTile, ret))
    {
      LOG(ERROR) << "Tile order not as expected index=" << tileIndex
		 << " belowIndex=" << belowTile;
      ret = _setToMotherOrColdstart(tileIndex, isMotherTile);
    }
    return ret;
  }

  double oBar;
  TileRange r = _parms._tileInfo.range(tileIndex);
  if (!_obsSetup(r, _parms._obsThreshold[threshIndex], obsGrid, oBar))
  {
    ret = _setToMotherOrColdstart(tileIndex, isMotherTile);
    return ret;
  }
  else
  {
    return TileObarInfo(oBar, tileIndex, isMotherTile, false);
  }
}

//-----------------------------------------------------------------------
bool ObarComputeMgr::_obsSetup(const TileRange &r,
			       double thresh,
			       const Grid &obsGrid,
			       double &oBar)
{
  bool outOfBounds;
  if (!obsGrid.percentAboveThresholdSubset(thresh,
					   r.getX0(), r.getY0(),
					   r.getNx(), r.getNy(),
					   true, false, oBar, outOfBounds))
  {
    if (outOfBounds)
    {
      LOG(ERROR) << "Tile " << r.sprint() << " goes out of bounds";
    }
    else
    {
      LOG(DEBUG_VERBOSE) << "Cannot compute percent obs data above threshold, all missing";
    }
    return false;
  }
  if (oBar == 0.0)
  {
    LOG(DEBUG_VERBOSE) << "No obs data above threshold " << thresh;
  }
  return true;
}

//-----------------------------------------------------------------------
TileObarInfo ObarComputeMgr::_setToMotherOrColdstart(int tileIndex,
						     bool isMotherTile)
{
  if (isMotherTile)
  {
    // this means setting the mother tile has failed
    _motherFail = true;
    _motherSet = false;
    return TileObarInfo(0.0, tileIndex, true, true);
  }
  else
  {
    if (_motherSet)
    {
      TileObarInfo ret(_motherInfo);
      ret.setTileIndex(tileIndex);
      return ret;
    }
    else
    {
      return TileObarInfo(0.0, tileIndex,false, true);
    }
  }
}