//+----------------------------------------------------------------------------+
//| Description:  Magic Set Editor - Program to make Magic (tm) cards          |
//| Copyright:    (C) Twan van Laarhoven and the other MSE developers          |
//| License:      GNU General Public License 2 or later (see file COPYING)     |
//+----------------------------------------------------------------------------+

// ----------------------------------------------------------------------------- : Includes

#include <util/prec.hpp>
#include <gui/symbol/selection.hpp>
#include <data/symbol.hpp>
#include <gfx/bezier.hpp>

// ----------------------------------------------------------------------------- : Selection

void SymbolPartsSelection::setSymbol(const SymbolP& symbol) {
  root = symbol.get();
  clear();
}

void SymbolPartsSelection::clear() {
  selection.clear();
}

bool SymbolPartsSelection::select(const SymbolPartP& part, SelectMode mode) {
  if (!part) return false;
  // make sure part is not the decendent of a part that is already selected
  if (mode != SELECT_OVERRIDE) {
    FOR_EACH(s, selection) {
      if (s != part && s->isAncestor(*part)) return false;
    }
  }
  // select
  if (mode == SELECT_OVERRIDE) {
    if (selection.size() == 1 && *selection.begin() == part) return false; // already selected
    selection.clear();
    selection.insert(part);
  } else if (mode == SELECT_IF_OUTSIDE) {
    if (selected(part)) {
      return false;
    } else {
      selection.clear();
      selection.insert(part);
    }
  } else {
    if (selected(part)) {
      selection.erase(part);
    } else {
      selection.insert(part);
      // make part is not the ancestor of a part that is already selected
      clearChildren(part.get());
    }
  }
  return true;
}

void SymbolPartsSelection::clearChildren(SymbolPart* part) {
  if (SymbolGroup* g = part->isSymbolGroup()) {
    FOR_EACH(p, g->parts) {
      if (selected(p)) selection.erase(p);
      clearChildren(p.get());
    }
  }
}

SymbolShapeP SymbolPartsSelection::getAShape() const {
  FOR_EACH(s, selection) {
    if (s->isSymbolShape()) return static_pointer_cast<SymbolShape>(s);
  }
  return SymbolShapeP();
}

SymbolSymmetryP SymbolPartsSelection::getASymmetry() const {
  FOR_EACH(s, selection) {
    if (s->isSymbolSymmetry()) return static_pointer_cast<SymbolSymmetry>(s);
  }
  return SymbolSymmetryP();
}


// ----------------------------------------------------------------------------- : Position based

SymbolPartP SymbolPartsSelection::find(const SymbolPartP& part, const Vector2D& pos) const {
  if (SymbolShape* s = part->isSymbolShape()) {
    if (point_in_shape(pos, *s)) return part;
  }
  if (SymbolGroup* g = part->isSymbolGroup()) {
    FOR_EACH(p, g->parts) {
      SymbolPartP found = find(p, pos);
      if (found) {
        if (part->isSymbolSymmetry() || selected(found)) {
          return found;
        } else {
          return part; // don't select inside groups
        }
      }
    }
  }
  return SymbolPartP();
}

SymbolPartP SymbolPartsSelection::find(const Vector2D& position) const {
  FOR_EACH(p, root->parts) {
    SymbolPartP found = find(p, position);
    if (found) return found;
  }
  return SymbolPartP();
}

// ----------------------------------------------------------------------------- : Rectangle based

bool SymbolPartsSelection::selectRect(const Vector2D& a, const Vector2D& b, const Vector2D& c) {
  return selectRect(*root, a, b, c);
}
bool SymbolPartsSelection::selectRect(const SymbolGroup& parent, const Vector2D& a, const Vector2D& b, const Vector2D& c) {
  bool changes = false;
  Bounds ab(a); ab.update(b);
  Bounds bc(b); bc.update(c);
  FOR_EACH_CONST(p, parent.parts) {
    bool in_ab = ab.contains(p->bounds);
    bool in_bc = bc.contains(p->bounds);
    if (in_ab != in_bc) {
      select(p, SELECT_TOGGLE);
      changes = true;
    } else if (SymbolGroup* g = p->isSymbolGroup()) {
      if (p->isSymbolSymmetry() || selected(p)) {
        selectRect(*g, a, b, c);
      }
    }
  }
  return changes;
}
