
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2016 Matthias Koefferlein

  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/



#include "extDiffToolDialog.h"
#include "rdb.h"
#include "dbLayoutDiff.h"
#include "dbRecursiveShapeIterator.h"
#include "dbEdgeProcessor.h"
#include "tlTimer.h"
#include "tlProgress.h"
#include "layCellView.h"
#include "layLayoutView.h"
#include "layExceptions.h"
#include "layMainWindow.h"

#include "DiffToolDialog.h"

#include <stdio.h>

namespace ext
{

std::string cfg_diff_run_xor ("diff-run-xor");
std::string cfg_diff_detailed ("diff-detailed");
std::string cfg_diff_smart ("diff-smart");
std::string cfg_diff_summarize ("diff-summarize");
std::string cfg_diff_expand_cell_arrays ("diff-expand-cell-arrays");
std::string cfg_diff_exact ("diff-exact");

// ------------------------------------------------------------------------------
//  RdbDifferenceReceiver definition

class RdbDifferenceReceiver
  : public db::DifferenceReceiver
{
public:
  RdbDifferenceReceiver (const db::Layout &la, const db::Layout &lb, rdb::Database *rdb, bool detailed, bool with_properties, bool run_xor);

  void dbu_differs (double dbu_a, double dbu_b);
  void layer_in_a_only (const db::LayerProperties &la);
  void layer_in_b_only (const db::LayerProperties &lb);
  void layer_name_differs (const db::LayerProperties &la, const db::LayerProperties &lb);
  void cell_in_a_only (const std::string &cellname, db::cell_index_type ci);
  void cell_in_b_only (const std::string &cellname, db::cell_index_type ci);
  void cell_name_differs (const std::string &cellname_a, db::cell_index_type cia, const std::string &cellname_b, db::cell_index_type cib);
  void bbox_differs (const db::Box &ba, const db::Box &bb);
  void begin_cell (const std::string &cellname, db::cell_index_type cia, db::cell_index_type cib);
  void begin_inst_differences ();
  void instances_in_a (const std::vector <db::CellInstArrayWithProperties> &insts_a, const std::vector <std::string> &cell_names, const db::PropertiesRepository &props);
  void instances_in_b (const std::vector <db::CellInstArrayWithProperties> &insts_b, const std::vector <std::string> &cell_names, const db::PropertiesRepository &props);
  void instances_in_a_only (const std::vector <db::CellInstArrayWithProperties> &anotb, const db::Layout &a);
  void instances_in_b_only (const std::vector <db::CellInstArrayWithProperties> &bnota, const db::Layout &b);
  void begin_layer (const db::LayerProperties &layer, unsigned int layer_index_a, bool is_valid_a, unsigned int layer_index_b, bool is_valid_b);
  void end_layer ();
  void per_layer_bbox_differs (const db::Box &ba, const db::Box &bb);
  void begin_polygon_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &a, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &b);
  void begin_path_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Path, db::properties_id_type> > &a, const std::vector <std::pair <db::Path, db::properties_id_type> > &b);
  void begin_box_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Box, db::properties_id_type> > &a, const std::vector <std::pair <db::Box, db::properties_id_type> > &b);
  void begin_edge_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b);
  void begin_text_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &b);

private:
  const db::Layout *mp_layout_a;
  const db::Layout *mp_layout_b;
  rdb::Database *mp_rdb;
  rdb::Cell *mp_cell;
  rdb::Cell *mp_topcell;
  rdb::Category *mp_general_cat;
  rdb::Category *mp_a_only_cat;
  rdb::Category *mp_b_only_cat;
  std::vector<rdb::Category *> mp_a_only_per_layer_cat;
  std::vector<rdb::Category *> mp_b_only_per_layer_cat;
  std::map<std::pair<int, int>, rdb::Category *> mp_xor_cat;
  std::vector<db::CellInstArrayWithProperties> m_insts_a, m_insts_b;

  std::string m_cellname;
  db::LayerProperties m_layer;
  unsigned int m_layer_index_a, m_layer_index_b;
  bool m_is_valid_layer_index_a, m_is_valid_layer_index_b;
  bool m_diffs_reported;
  bool m_with_properties;
  bool m_detailed;
  bool m_run_xor;
  db::ShapeProcessor m_ep;
  size_t m_obj_index;

  void produce_cell_inst (const db::CellInstArrayWithProperties &ci, const db::Layout *layout, const rdb::Category *cat);
  template <class SH> void produce_diffs_for_xor (const db::PropertiesRepository &pr, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b, double dbu_a, db::Shapes &shapes);
  template <class SH> void produce_diffs (const db::PropertiesRepository &pr, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b, double dbu_a, const rdb::Category *cat);
  template <class SH> void shape_diffs (const db::PropertiesRepository &pr, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b);
  void shape_diffs_found ();
};

RdbDifferenceReceiver::RdbDifferenceReceiver (const db::Layout &layout_a, const db::Layout &layout_b, rdb::Database *rdb, bool detailed, bool with_properties, bool run_xor)
  : mp_layout_a (&layout_a), 
    mp_layout_b (&layout_b), 
    mp_rdb (rdb), 
    mp_cell (0), 
    m_cellname (), 
    m_layer (), 
    m_layer_index_a (0), 
    m_layer_index_b (0), 
    m_is_valid_layer_index_a (false), 
    m_is_valid_layer_index_b (false), 
    m_diffs_reported (false), 
    m_with_properties (with_properties), 
    m_detailed (detailed),
    m_run_xor (run_xor)
{
  mp_topcell = rdb->create_cell ("" /*dummy*/);
  mp_general_cat = rdb->create_category ("Summary");
  mp_general_cat->set_description (tl::to_string (QObject::tr ("Summary of Differences")));
  m_obj_index = 0;

  std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc> layers;
  for (db::Layout::layer_iterator l = layout_a.begin_layers (); l != layout_a.end_layers (); ++l) {
    layers.insert (std::make_pair (*(*l).second, std::make_pair (-1, -1))).first->second.first = (*l).first;
  }
  for (db::Layout::layer_iterator l = layout_b.begin_layers (); l != layout_b.end_layers (); ++l) {
    layers.insert (std::make_pair (*(*l).second, std::make_pair (-1, -1))).first->second.second = (*l).first;
  }

  if (detailed) {

    rdb::Category *instances_cat = rdb->create_category ("Instances");
    instances_cat->set_description (tl::to_string (QObject::tr ("Differences in instances")));

    mp_a_only_cat = rdb->create_category (instances_cat, "A");
    mp_a_only_cat->set_description (tl::to_string (QObject::tr ("Instances in A but not in B")));

    mp_b_only_cat = rdb->create_category (instances_cat, "B");
    mp_b_only_cat->set_description (tl::to_string (QObject::tr ("Instances in B but not in A")));

    for (std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc>::const_iterator l = layers.begin (); l != layers.end (); ++l) {

      while ((int) mp_a_only_per_layer_cat.size () <= l->second.first) {
        mp_a_only_per_layer_cat.push_back (0);
      }
      while ((int) mp_b_only_per_layer_cat.size () <= l->second.second) {
        mp_b_only_per_layer_cat.push_back (0);
      }

      rdb::Category *layer_cat = rdb->create_category (l->first.to_string ());
      layer_cat->set_description (tl::to_string (QObject::tr ("Differences in layer")) + " " + l->first.to_string ());

      if (l->second.first >= 0) {
        mp_a_only_per_layer_cat [(unsigned int) l->second.first] = rdb->create_category (layer_cat, "A");
        mp_a_only_per_layer_cat [(unsigned int) l->second.first]->set_description (tl::to_string (QObject::tr ("Shapes in A but not in B, on Layer ")) + l->first.to_string ());
      }

      if (l->second.second >= 0) {
        mp_b_only_per_layer_cat [(unsigned int) l->second.second] = rdb->create_category (layer_cat, "B");
        mp_b_only_per_layer_cat [(unsigned int) l->second.second]->set_description (tl::to_string (QObject::tr ("Shapes in B but not in A, on Layer ")) + l->first.to_string ());
      }

    }

  } else {
    mp_a_only_cat = 0;
    mp_b_only_cat = 0;
  }

  if (run_xor) {

    for (std::map<db::LayerProperties, std::pair<int, int>, db::LPLogicalLessFunc>::const_iterator l = layers.begin (); l != layers.end (); ++l) {

      rdb::Category *cat = rdb->create_category (std::string ("XOR ") + l->first.to_string ());
      cat->set_description (tl::to_string (QObject::tr ("Geometrical differences on layer")) + " " + l->first.to_string ());
      mp_xor_cat [l->second] = cat;

    }

  }
}

static void 
add_property_text (rdb::Item *item, const db::PropertiesRepository &pr, db::properties_id_type prop_id) 
{
  if (prop_id != 0) {
    const db::PropertiesRepository::properties_set &p = pr.properties (prop_id);
    for (db::PropertiesRepository::properties_set::const_iterator pp = p.begin (); pp != p.end (); ++pp) {
      const tl::Variant &name = pr.prop_name (pp->first);
      std::string r = std::string ("property: ") + name.to_string () + " = " + pp->second.to_string ();
      item->add_value (r);
    }
  }
}

void 
RdbDifferenceReceiver::produce_cell_inst (const db::CellInstArrayWithProperties &ci, const db::Layout *layout, const rdb::Category *cat)
{
  db::box_convert <db::CellInstArrayWithProperties> bc (*layout);

  rdb::Item *item = mp_rdb->create_item (mp_cell->id (), cat->id ());

  std::string r = "item: " + tl::sprintf (tl::to_string (QObject::tr ("instance: (%s) %s")), layout->cell_name (ci.object ().cell_index ()), ci.complex_trans ().to_string ());

  db::Point a, b;
  unsigned long amax, bmax;
  if (ci.is_regular_array (a, b, amax, bmax)) {
    r += tl::sprintf (" [a=%s, b=%s, na=%ld, nb=%ld]", a.to_string (), b.to_string (), amax, bmax);
  }

  item->add_value (r);

  db::Box box = bc (ci);
  item->add_value (box * layout->dbu ());

  if (m_with_properties) {
    add_property_text (item, layout->properties_repository (), ci.properties_id ()); 
  }
}

void
RdbDifferenceReceiver::dbu_differs (double dbu_a, double dbu_b) 
{
  rdb::Item *item = mp_rdb->create_item (mp_topcell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Database units differ %g vs. %g")), dbu_a, dbu_b));
}

void 
RdbDifferenceReceiver::layer_in_a_only (const db::LayerProperties &la)
{
  rdb::Item *item = mp_rdb->create_item (mp_topcell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Layer %s is not present in layout B, but in A")), la.to_string ()));
}

void 
RdbDifferenceReceiver::layer_in_b_only (const db::LayerProperties &lb)
{
  rdb::Item *item = mp_rdb->create_item (mp_topcell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Layer %s is not present in layout A, but in B")), lb.to_string ()));
}

void
RdbDifferenceReceiver::layer_name_differs (const db::LayerProperties &la, const db::LayerProperties &lb)
{
  rdb::Item *item = mp_rdb->create_item (mp_topcell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Layer names differ between layout A and B for layer %d/%d: %s vs. %s")), la.layer, la.datatype, la.name, lb.name));
}

void 
RdbDifferenceReceiver::cell_in_a_only (const std::string &cellname, db::cell_index_type ci)
{
  rdb::Item *item = mp_rdb->create_item (mp_topcell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Cell %s is not present in layout B, but in A")), cellname));
}

void 
RdbDifferenceReceiver::cell_in_b_only (const std::string &cellname, db::cell_index_type ci)
{
  rdb::Item *item = mp_rdb->create_item (mp_topcell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Cell %s is not present in layout A, but in B")), cellname));
}

void 
RdbDifferenceReceiver::cell_name_differs (const std::string &cellname_a, db::cell_index_type cia, const std::string &cellname_b, db::cell_index_type cib)
{
  rdb::Item *item = mp_rdb->create_item (mp_topcell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Cell %s in A is renamed to %s in B")), cellname_a, cellname_b));
}

void 
RdbDifferenceReceiver::begin_cell (const std::string &cellname, db::cell_index_type cia, db::cell_index_type cib)
{
  mp_cell = mp_rdb->create_cell (cellname);
  m_diffs_reported = false;

  m_insts_a.clear ();
  m_insts_b.clear ();
}

void 
RdbDifferenceReceiver::bbox_differs (const db::Box &ba, const db::Box &bb)
{
  rdb::Item *item = mp_rdb->create_item (mp_cell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Bounding boxes differ: %s (A) vs. %s (B)")), ba.to_string (), bb.to_string ()));
}

void
RdbDifferenceReceiver::begin_inst_differences ()
{
  rdb::Item *item = mp_rdb->create_item (mp_cell->id (), mp_general_cat->id ());
  item->add_value (tl::to_string (QObject::tr ("Instances differ")));
}

void
RdbDifferenceReceiver::instances_in_a (const std::vector <db::CellInstArrayWithProperties> &insts_a, const std::vector <std::string> &cell_names, const db::PropertiesRepository &props)
{
  // .. nothing ..
}

void
RdbDifferenceReceiver::instances_in_b (const std::vector <db::CellInstArrayWithProperties> &insts_b, const std::vector <std::string> &cell_names, const db::PropertiesRepository &props)
{
  // .. nothing ..
}

void
RdbDifferenceReceiver::instances_in_a_only (const std::vector <db::CellInstArrayWithProperties> &anotb, const db::Layout &a)
{
  if (m_detailed) {
    for (std::vector <db::CellInstArrayWithProperties>::const_iterator s = anotb.begin (); s != anotb.end (); ++s) {
      produce_cell_inst (*s, mp_layout_a, mp_a_only_cat);
    }
  }

  if (m_run_xor) {
    m_insts_a.insert (m_insts_a.end (), anotb.begin (), anotb.end ());
  }
}

void
RdbDifferenceReceiver::instances_in_b_only (const std::vector <db::CellInstArrayWithProperties> &bnota, const db::Layout &b)
{
  if (m_detailed) {
    for (std::vector <db::CellInstArrayWithProperties>::const_iterator s = bnota.begin (); s != bnota.end (); ++s) {
      produce_cell_inst (*s, mp_layout_b, mp_b_only_cat);
    }
  }

  if (m_run_xor) {
    m_insts_b.insert (m_insts_b.end (), bnota.begin (), bnota.end ());
  }
}

void
RdbDifferenceReceiver::begin_layer (const db::LayerProperties &layer, unsigned int layer_index_a, bool is_valid_a, unsigned int layer_index_b, bool is_valid_b)
{
  m_layer = layer;
  m_diffs_reported = false;
  m_layer_index_a = layer_index_a;
  m_is_valid_layer_index_a = is_valid_a;
  m_layer_index_b = layer_index_b;
  m_is_valid_layer_index_b = is_valid_b;

  if (m_run_xor) {

    m_obj_index = 0;

    if (is_valid_a) {
      for (std::vector <db::CellInstArrayWithProperties>::const_iterator i = m_insts_a.begin (); i != m_insts_a.end (); ++i) {
        for (db::RecursiveShapeIterator shapes (*mp_layout_a, mp_layout_a->cell (i->object ().cell_index ()), m_layer_index_a); ! shapes.at_end (); ++shapes) {
          for (db::CellInstArray::iterator a = i->begin (); ! a.at_end (); ++a) {
            m_ep.insert (shapes.shape (), i->complex_trans (*a) * shapes.trans (), m_obj_index * 2);
            ++m_obj_index;
          }
        }
      }
    }

    if (is_valid_b) {
      for (std::vector <db::CellInstArrayWithProperties>::const_iterator i = m_insts_b.begin (); i != m_insts_b.end (); ++i) {
        for (db::RecursiveShapeIterator shapes (*mp_layout_b, mp_layout_b->cell (i->object ().cell_index ()), m_layer_index_b); ! shapes.at_end (); ++shapes) {
          for (db::CellInstArray::iterator a = i->begin (); ! a.at_end (); ++a) {
            m_ep.insert (shapes.shape (), i->complex_trans (*a) * shapes.trans (), m_obj_index * 2 + 1);
            ++m_obj_index;
          }
        }
      }
    }

  }
}

void
RdbDifferenceReceiver::end_layer ()
{
  if (m_run_xor) {

    std::vector <db::Polygon> out_polygons;
    db::BooleanOp op (db::BooleanOp::Xor);
    db::PolygonContainer pc (out_polygons);
    db::PolygonGenerator out (pc, false /*don't resolve holes*/, true /*min coherence*/);
    m_ep.process (out, op);

    db::CplxTrans t (mp_layout_a->dbu ());

    rdb::Category *cat = mp_xor_cat [std::make_pair (m_is_valid_layer_index_a ? m_layer_index_a : -1, m_is_valid_layer_index_b ? m_layer_index_b : -1)];
    if (cat) {
      for (std::vector <db::Polygon>::const_iterator x = out_polygons.begin (); x != out_polygons.end (); ++x) {
        rdb::Item *item = mp_rdb->create_item (mp_cell->id (), cat->id ());
        item->add_value (t * *x);
      }
    }

    m_ep.clear ();

  }
}

void 
RdbDifferenceReceiver::per_layer_bbox_differs (const db::Box &ba, const db::Box &bb)
{
  rdb::Item *item = mp_rdb->create_item (mp_cell->id (), mp_general_cat->id ());
  item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Per-layer bounding boxes differ (layer %s): %s (A) vs. %s (B)")), m_layer.to_string (), ba.to_string (), bb.to_string ()));
}

void
RdbDifferenceReceiver::shape_diffs_found ()
{
  if (! m_diffs_reported) {
    rdb::Item *item = mp_rdb->create_item (mp_cell->id (), mp_general_cat->id ());
    item->add_value (tl::sprintf (tl::to_string (QObject::tr ("Shapes differ on layer %s")), m_layer.to_string ()));
    m_diffs_reported = true;
  }
}

void
RdbDifferenceReceiver::begin_polygon_differences ()
{
  shape_diffs_found ();
}

inline std::string shape_type (const db::Polygon &) 
{
  return "polygon";
}

inline std::string shape_type (const db::Path &) 
{
  return "path";
}

inline std::string shape_type (const db::Edge &) 
{
  return "edge";
}

inline std::string shape_type (const db::Text &) 
{
  return "text";
}

inline std::string shape_type (const db::Box &) 
{
  return "box";
}

template <class SH>
void
RdbDifferenceReceiver::produce_diffs_for_xor (const db::PropertiesRepository &pr, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b, double dbu_a, db::Shapes &shapes)
{
  db::CplxTrans t (dbu_a);
  std::vector <std::pair <SH, db::properties_id_type> > anotb;
  std::set_difference (a.begin (), a.end (), b.begin (), b.end (), std::back_inserter (anotb));
  for (typename std::vector <std::pair <SH, db::properties_id_type> >::const_iterator s = anotb.begin (); s != anotb.end (); ++s) {
    shapes.insert (s->first);
  }
}

template <class SH>
void
RdbDifferenceReceiver::produce_diffs (const db::PropertiesRepository &pr, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b, double dbu_a, const rdb::Category *cat)
{
  db::CplxTrans t (dbu_a);
  std::vector <std::pair <SH, db::properties_id_type> > anotb;
  std::set_difference (a.begin (), a.end (), b.begin (), b.end (), std::back_inserter (anotb));
  for (typename std::vector <std::pair <SH, db::properties_id_type> >::const_iterator s = anotb.begin (); s != anotb.end (); ++s) {

    rdb::Item *item = mp_rdb->create_item (mp_cell->id (), cat->id ());

    if (s->second && m_with_properties) {
      item->add_value ("item: " + shape_type (s->first) + " " + tl::to_string (QObject::tr ("with properties")));
    } else {
      item->add_value ("item: " + shape_type (s->first));
    }

    item->add_value (t * s->first);

    if (s->second && m_with_properties) {
      add_property_text (item, pr, s->second); 
    }

  }
}

template <class SH> 
void 
RdbDifferenceReceiver::shape_diffs (const db::PropertiesRepository &pr, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b)
{
  if (m_detailed && m_is_valid_layer_index_a && mp_a_only_per_layer_cat [m_layer_index_a] != 0) {
    produce_diffs (pr, a, b, mp_layout_a->dbu (), mp_a_only_per_layer_cat [m_layer_index_a]);
  }

  if (m_run_xor && m_is_valid_layer_index_a) {
    db::Shapes shapes;
    produce_diffs_for_xor (pr, a, b, mp_layout_a->dbu (), shapes);
    for (db::Shapes::shape_iterator s = shapes.begin (db::Shapes::shape_iterator::All); ! s.at_end (); ++s) {
      m_ep.insert (*s, m_obj_index * 2);
      ++m_obj_index;
    }
  }

  if (m_detailed && m_is_valid_layer_index_b && mp_b_only_per_layer_cat [m_layer_index_b] != 0) {
    produce_diffs (pr, b, a, mp_layout_b->dbu (), mp_b_only_per_layer_cat [m_layer_index_b]);
  }

  if (m_run_xor && m_is_valid_layer_index_b) {
    db::Shapes shapes;
    produce_diffs_for_xor (pr, b, a, mp_layout_b->dbu (), shapes);
    for (db::Shapes::shape_iterator s = shapes.begin (db::Shapes::shape_iterator::All); ! s.at_end (); ++s) {
      m_ep.insert (*s, m_obj_index * 2 + 1);
      ++m_obj_index;
    }
  }
}

void
RdbDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &a, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &b)
{
  shape_diffs (pr, a, b);
}

void
RdbDifferenceReceiver::begin_path_differences ()
{
  shape_diffs_found ();
}

void
RdbDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Path, db::properties_id_type> > &a, const std::vector <std::pair <db::Path, db::properties_id_type> > &b)
{
  shape_diffs (pr, a, b);
}

void
RdbDifferenceReceiver::begin_box_differences ()
{
  shape_diffs_found ();
}

void
RdbDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Box, db::properties_id_type> > &a, const std::vector <std::pair <db::Box, db::properties_id_type> > &b)
{
  shape_diffs (pr, a, b);
}

void
RdbDifferenceReceiver::begin_edge_differences ()
{
  shape_diffs_found ();
}

void
RdbDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b)
{
  shape_diffs (pr, a, b);
}

void
RdbDifferenceReceiver::begin_text_differences ()
{
  shape_diffs_found ();
}

void
RdbDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &b)
{
  shape_diffs (pr, a, b);
}

// ------------------------------------------------------------------------------
//  DiffToolDialog definition

DiffToolDialog::DiffToolDialog (QWidget *parent)
  : mp_view (0)
{
  mp_ui = new Ui::DiffToolDialog ();
  mp_ui->setupUi (this);

  connect (mp_ui->xor_cbx, SIGNAL (clicked ()), this, SLOT (xor_changed ()));
}

DiffToolDialog::~DiffToolDialog ()
{
  delete mp_ui;
  mp_ui = 0;
}

int 
DiffToolDialog::exec (lay::LayoutView *view)
{
  mp_view = view;

  if (view != mp_ui->layouta->layout_view () || view != mp_ui->layoutb->layout_view ()) {

    mp_ui->layouta->set_layout_view (view);
    mp_ui->layoutb->set_layout_view (view);

    if (view->cellviews () >= 2) {
      mp_ui->layouta->set_current_cv_index (0);
      mp_ui->layoutb->set_current_cv_index (1);
    }

  } else {
    //  force update of the layer list
    //  TODO: the controls should register a listener for the view so this activity is not necessary:
    mp_ui->layouta->set_layout_view (view);
    mp_ui->layoutb->set_layout_view (view);
  }

  lay::PluginRoot *config_root = lay::MainWindow::instance ();

  bool f = false;
  if (config_root->config_get (cfg_diff_run_xor, f)) {
    mp_ui->xor_cbx->setChecked (f);
  }
  if (config_root->config_get (cfg_diff_detailed, f)) {
    mp_ui->detailed_cbx->setChecked (f);
  }
  if (config_root->config_get (cfg_diff_smart, f)) {
    mp_ui->smart_cbx->setChecked (f);
  }
  if (config_root->config_get (cfg_diff_summarize, f)) {
    mp_ui->summarize_cbx->setChecked (f);
  }
  if (config_root->config_get (cfg_diff_expand_cell_arrays, f)) {
    mp_ui->expand_cell_arrays_cbx->setChecked (f);
  }
  if (config_root->config_get (cfg_diff_exact, f)) {
    mp_ui->exact_cbx->setChecked (f);
  }

  update ();

  int ret = QDialog::exec ();

  if (ret) {
    run_diff ();
  }

  mp_view = 0;
  return ret;
}

void 
DiffToolDialog::accept ()
{
BEGIN_PROTECTED

  int cv_index_a = mp_ui->layouta->current_cv_index ();
  int cv_index_b = mp_ui->layoutb->current_cv_index ();

  const lay::CellView &cva = mp_view->cellview (cv_index_a);
  const lay::CellView &cvb = mp_view->cellview (cv_index_b);

  if (&cva->layout () == &cvb->layout () && cva.cell_index () == cvb.cell_index ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("Trying to perform an Diff between identical layouts")));  
  }

  lay::PluginRoot *config_root = lay::MainWindow::instance ();

  config_root->config_set (cfg_diff_run_xor, mp_ui->xor_cbx->isChecked ());
  config_root->config_set (cfg_diff_detailed, mp_ui->detailed_cbx->isChecked ());
  config_root->config_set (cfg_diff_smart, mp_ui->smart_cbx->isChecked ());
  config_root->config_set (cfg_diff_summarize, mp_ui->summarize_cbx->isChecked ());
  config_root->config_set (cfg_diff_expand_cell_arrays, mp_ui->expand_cell_arrays_cbx->isChecked ());
  config_root->config_set (cfg_diff_exact, mp_ui->exact_cbx->isChecked ());
  config_root->config_end ();

  QDialog::accept ();

END_PROTECTED
}

void
DiffToolDialog::update ()
{
  bool xor_mode = mp_ui->xor_cbx->isChecked ();
  mp_ui->summarize_cbx->setEnabled (! xor_mode);
  mp_ui->detailed_cbx->setEnabled (! xor_mode);
  mp_ui->expand_cell_arrays_cbx->setEnabled (! xor_mode);
  mp_ui->exact_cbx->setEnabled (! xor_mode);
}

void 
DiffToolDialog::run_diff ()
{
  bool smart = mp_ui->smart_cbx->isChecked ();
  bool run_xor = mp_ui->xor_cbx->isChecked ();
  bool detailed = !run_xor && mp_ui->detailed_cbx->isChecked ();
  bool summarize = !run_xor && mp_ui->summarize_cbx->isChecked ();
  bool expand_cell_arrays = !run_xor && mp_ui->expand_cell_arrays_cbx->isChecked ();
  bool exact = !run_xor && mp_ui->exact_cbx->isChecked ();

  int cv_index_a = mp_ui->layouta->current_cv_index ();
  int cv_index_b = mp_ui->layoutb->current_cv_index ();

  lay::CellView cva = mp_view->cellview (cv_index_a);
  lay::CellView cvb = mp_view->cellview (cv_index_b);

  unsigned int flags = 0;
  if (detailed || run_xor) {
    flags |= db::layout_diff::f_verbose;
  }
  if (!exact) {
    flags |= db::layout_diff::f_no_text_details;
    flags |= db::layout_diff::f_no_layer_names;
    flags |= db::layout_diff::f_no_text_orientation;
    flags |= db::layout_diff::f_no_properties;
    flags |= db::layout_diff::f_boxes_as_polygons;
    flags |= db::layout_diff::f_paths_as_polygons;
  }
  if (expand_cell_arrays) {
    flags |= db::layout_diff::f_flatten_array_insts;
  }
  if (! summarize) {
    flags |= db::layout_diff::f_dont_summarize_missing_layers;
  }
  if (smart) {
    flags |= db::layout_diff::f_smart_cell_mapping;
  }

  //  Create the report database or identify the output layout
  rdb::Database *rdb = 0;
  int rdb_index = 0;

  rdb = new rdb::Database ();
  rdb->set_name ("Diff " + cva->name () + "/" + cvb->name ());
  rdb->set_top_cell_name (cva->layout ().cell_name (cva.cell_index ()));

  rdb_index = mp_view->add_rdb (rdb);

  std::string srca = cva->name () + ", Cell " + cva->layout ().cell_name (cva.cell_index ());
  std::string srcb = cvb->name () + ", Cell " + cvb->layout ().cell_name (cvb.cell_index ());
  rdb->set_description ("Diff of '" + srca + "' vs. '" + srcb + "'");

  RdbDifferenceReceiver r (cva->layout (), cvb->layout (), rdb, detailed, exact, run_xor);

  db::compare_layouts (cva->layout (), cva.cell_index (), cvb->layout (), cvb.cell_index (), flags, r);

  mp_view->open_rdb_browser (rdb_index, cv_index_a);
  mp_view->update_content ();
}

}

