#include "RegionGraph.h"

using namespace std;
using namespace NICE;
using namespace OBJREC;

Node::Node(int n)
{
  atborder = false;
  number = n;
  size = 0;
  amountx = 0;
  amounty = 0;
  maxx = -numeric_limits<int>::max();
  maxy = -numeric_limits<int>::max();
  minx = numeric_limits<int>::max();
  miny = numeric_limits<int>::max();
}

void Node::addNeighbor(Node *nb)
{
  neighbors.push_back(nb);

  int l = nb->getLabel();

  if (l != label)
    atborder = true;
}

void Node::getRect(int &x0, int &y0, int &x1, int &y1)
{
  x0 = minx;
  x1 = maxx;
  y0 = miny;
  y1 = maxy;
}

void Node::change(int nblabel)
{
  if (nblabel != label)
    atborder = true;
  else
  {
    atborder = false;
    for (int i = 0; i < (int) neighbors.size(); i++)
    {
      if (neighbors[i]->getLabel() != label)
        atborder = true;
    }
  }
}

bool Node::isAtBorder()
{
  return atborder;
}

void Node::setLabel(int l)
{
  label = l;
}

int Node::getLabel() const
{
  return label;
}

void Node::getNeighbors(vector<Node*> &nb) const
{
  nb = neighbors;
}

int Node::getRegion() const
{
  return number;
}

void Node::incSize(int s)
{
  size += s;
}

void Node::addPos(int x, int y)
{
  amountx += x;
  amounty += y;
  minx = std::min(minx, x);
  miny = std::min(miny, y);
  maxx = std::max(maxx, x);
  maxy = std::max(maxy, y);
}

void Node::getCentroid(int &x, int &y) const
{
  x = amountx / size;
  y = amounty / size;
}

int Node::getSize() const
{
  return size;
}

int Node::getSpan() const
{
  return std::max(maxx - minx, maxy - miny);
}

int Node::store(ofstream & fout)
{
  fout << number << " "  << size << " " << amountx / size << " " << amounty / size << " " << minx << " " << miny << " " << maxx << " " << maxy << endl;
  for (uint i = 0; i < neighbors.size(); i++)
  {
    fout << neighbors[i]->getNumber() << " ";
  }
  fout << endl;
  for (uint i = 0; i < probs.size(); i++)
  {
    fout << probs[i] << " ";
  }
  fout << endl;
}

void Node::setProbs(vector<double> _probs)
{
  probs = _probs;
}

RegionGraph::~RegionGraph()
{
  for (int i = 0; i < (int)nodes.size();i++)
  {
    delete nodes[i];
  }
}

void RegionGraph::computeGraph(const Examples &regions, const NICE::Matrix &mask)
{
  int rs = (int)regions.size();
  computeGraph(mask, rs);

  for (int i = 0; i < rs; i++)
  {
    nodes[i]->setLabel(regions[i].first);
  }
}

void RegionGraph::get(vector<Node*> &n) const
{
  n = nodes;
}

void RegionGraph::computeGraph(const NICE::Matrix &mask, const int &rgcount)
{
  vector<set<int> > nbs;
  for (int i = 0; i < rgcount; i++)
  {
    Node * a = new Node(i);
    nodes.push_back(a);
    nodes[i]->setLabel(0);
    set<int> nb;
    nbs.push_back(nb);
  }

  for (int y = 0; y < (int)mask.cols(); y++)
  {
    for (int x = 0; x < (int)mask.rows(); x++)
    {
      int val = mask(x, y);

      for (int i = -1; i < 2; i++)
      {
        if (x + i < 0 || x + i >= (int)mask.rows())
          continue;
        for (int j = -1; j < 2; j++)
        {
          if (y + j < 0 || y + j >= (int)mask.cols())
            continue;

          int val2 = mask(x + i, y + j);

          if (val2 != val)
          {
            nbs[val].insert(val2);
          }
        }
      }
      nodes[val]->incSize();
      nodes[val]->addPos(x, y);
    }
  }

  for (int i = 0; i < rgcount; i++)
  {
    for ( set<int>::const_iterator iter = nbs[i].begin();iter != nbs[i].end();++iter)
    {
      nodes[i]->addNeighbor(nodes[*iter]);
    }
  }
}

int RegionGraph::size() const
{
  return (int)nodes.size();
}

Node* RegionGraph::operator[](int i) const
{
  return nodes[i];
}

void RegionGraph::write(string file)
{
  ofstream fout(file.c_str());

  fout << nodes.size() << endl;

  for (int i = 0; i < nodes.size(); i++)
  {
    nodes[i]->store(fout);
  }
  fout.close();

  return;
}