/***************************************************************************
**
**  This file is part of SciFigs.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2002-05-24
**  Copyright: 2002-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpGuiTools.h>

#include "Symbol.h"
#include "Pen.h"
#include "Legend.h"

namespace SciFigs {

const QString Legend::xmlLegendTag="Legend";

// Automatic registration of Legend upon loading of the library
REGISTER_METATYPE(Legend)

Legend::~Legend()
{
  TRACE;
  delete _textLookup;
}

Legend& Legend::operator=(const Legend& o)
{
  TRACE;
  _items=o._items;
  resetTextLookup();
  return *this;
}

bool Legend::operator==(const Legend& o) const
{
  TRACE;
  int n=_items.count();
  if(n!=o._items.count()) return false;
  for(int i=0;i < n;i++ ) {
    if(_items[i]!=o._items[i]) return false;
  }
  return true;
}

/*!
  Compares the number of items. A bit useless function but required for QMetaType::registerComparators()
*/
bool Legend::operator<(const Legend& o) const
{
  TRACE;
  return _items.count()<o._items.count();
}

void Legend::resize(int n)
{
  TRACE;
  _items.resize(n);
  resetTextLookup();
}

void Legend::clear()
{
  TRACE;
  _items.clear();
  resetTextLookup();
}

void Legend::resetTextLookup()
{
  TRACE;
  if(_textLookup) {
    delete _textLookup;
    _textLookup=0;
  }
}

ColorPalette Legend::palette() const
{
  TRACE;
  ColorPalette pal;
  int n=count();
  pal.resize(n);
  for(int i=0; i<n; i++) {
    coreColor(_items[i].pen().color(), pal.color(i));
  }
  return pal;
}

void Legend::setPalette(const ColorPalette& pal)
{
  TRACE;
  int n=pal.count();
  resize(n);
  QColor col;
  for(int i=0; i<n; i++) {
    guiColor(pal.color(i), col);
    _items[i].pen().setColor(col);
  }
}

void Legend::generateColorScale(int n, ColorPalette::Model m,
                                bool reversed, int transparency)
{
  TRACE;
  ColorPalette pal;
  pal.generateColorScale(n, m, reversed, transparency);
  setPalette(pal);
}

void Legend::generateGrayScale(int n, ColorPalette::Model m,
                               bool reversed, int transparency)
{
  TRACE;
  ColorPalette pal;
  pal.generateGrayScale(n, m, reversed, transparency);
  setPalette(pal);
}

void Legend::penColorToSymbolPen()
{
  TRACE;
  for(int i=0;i < _items.count();i++ ) {
    Pen p=_items[ i ].symbol().pen();
    p.setColor(_items[ i ].pen().color());
    _items[ i ].symbol().setPen(p);
  }
}

void Legend::penColorToSymbolBrush()
{
  TRACE;
  for(int i=0;i < _items.count();i++ ) {
    Brush b=_items[ i ].symbol().brush();
    b.setColor(_items[ i ].pen().color());
    _items[ i ].symbol().setBrush(b);
  }
}

void Legend::penColorToText()
{
  TRACE;
  for(int i=0;i < _items.count();i++ ) {
    _items[ i ].setTextColor(_items[ i ].pen().color());
  }
}

const LegendItem& Legend::item(int i)
{
  TRACE;
  if(i >= _items.count()) i=_items.count() - 1;
  return _items.at(i);
}

const QString& Legend::text(int i) const
{
  TRACE;
  if(i >= _items.count()) i=_items.count() - 1;
  return _items[ i ].text();
}

const QColor& Legend::textColor(int i) const
{
  TRACE;
  if(i >= _items.count()) i=_items.count() - 1;
  return _items[ i ].textColor();
}

QStringList Legend::texts() const
{
  TRACE;
  QStringList list;
  for(int i=0;i < _items.count();i++ ) list.append(_items[ i ].text());
  return list;
}

void Legend::setText(int i, QString t)
{
  TRACE;
  if(i >= _items.count()) {
    resize(i+1);
  }
  _items[i].setText(t);
  resetTextLookup();
}

void Legend::setTextColor(int i, QColor c)
{
  TRACE;
  if(i >= _items.count()) {
    resize(i+1);
  }
  _items[i].setTextColor(c);
}

void Legend::setPen(int i, Pen p)
{
  TRACE;
  if(i >= _items.count()) {
    resize(i+1);
  }
  _items[i].pen()=p;
}

const Pen& Legend::pen(int i) const
{
  TRACE;
  if(i >= _items.count()) i=_items.count() - 1;
  if(_items.count() > 0) {
    return _items[i].pen();
  } else {
    return Pen::null;
  }
}

/*!
  Returns the index of item with \a text. This is a fast lookup
  with a map. The lookup map is initialized at first call.
  Once called, the lookup map is maintained by other operations.
  If there are various items with the same name, the returned index may
  be random (depends upon lookup initialization and maintenance).
*/
int Legend::indexOf(const QString& text) const
{
  TRACE;  
  if(!_textLookup) {
    _textLookup=new QMap<QString, int>;
    for(int i=_items.count()-1; i >=0; i--) {
      _textLookup->insert(_items[ i ].text(), i);
    }
  }
  QMap<QString, int>::const_iterator it;
  it=_textLookup->find(text);
  if(it!=_textLookup->end()) {
    return it.value();
  } else {
    return -1;
  }
}

/*!
  Sets legend items to match \a tests list. All items not in \a texts are removed and all \a texts not
  available in items are added at the end with the \a defaultPen and the \a defaultSymbol.
*/
void Legend::setTexts(const QStringList& texts, const Pen& defaultPen, const Symbol& defaultSymbol)
{
  TRACE;
  foreach(QString text, texts) {
    int index=indexOf(text);
    if(index==-1) {
      index=count();
      resize(index+1);
      setText(index, text);
      setPen(index, defaultPen);
      setSymbol(index, defaultSymbol);
    }
  }
  // Eventually remove unsused items, do not use erase() which is likely to
  // be unefficient if there are many entries to remove.
  if(texts.count()<count()) {
    QSet<QString> textSet;
    foreach(QString text, texts) {
      textSet.insert(text);
    }
    QVector<LegendItem> newItems;
    int n=count();
    newItems.reserve(n);
    for(int i=0; i<n; i++) {
      if(textSet.contains(text(i))) {
        newItems.append(_items.at(i));
      }
    }
    _items=newItems;
    resetTextLookup();
  }
}

const Pen& Legend::pen(const QString& text) const
{
  TRACE;
  int i=indexOf(text);
  if(i > -1) {
    return _items[ i ].pen();
  } else {
    return Pen::null;
  }
}

const Symbol& Legend::symbol(int i) const
{
  TRACE;
  if(i >= _items.count()) i=_items.count() - 1;
  if(_items.count() > 0) {
    return _items[ i ].symbol();
  } else {
    return Symbol::null;
  }
}

void Legend::setSymbol(int i, Symbol s)
{
  TRACE;
  if(i >= _items.count()) {
    resize(i+1);
  }
  _items[ i ].symbol()=s;
}

const Symbol& Legend::symbol(const QString& text) const
{
  TRACE;
  int i=indexOf(text);
  if(i > -1) {
    return _items[ i ].symbol();
  } else {
    return Symbol::null;
  }
}

void Legend::setTexts(const Legend& o)
{
  TRACE;
  int n=count();
  if(n > o.count()) n=o.count();
  for(int i=0; i<n; i++ ) {
    _items[ i ].setText(o.text(i) );
  }
  resetTextLookup();
}

void Legend::setTextColors(const Legend& o)
{
  TRACE;
  int n=count();
  if(n > o.count()) n=o.count();
  for(int i=0; i<n; i++ ) {
    _items[ i ].setTextColor(o.textColor(i) );
  }
}

void Legend::setPens(const Legend& o)
{
  TRACE;
  int n=count();
  if(n > o.count()) n=o.count();
  for(int i=0; i<n; i++ ) {
    _items[ i ].setPen(o.pen(i) );
  }
}

void Legend::setSymbols(const Legend& o)
{
  TRACE;
  int n=count();
  if(n > o.count()) n=o.count();
  for(int i=0; i<n; i++ ) {
    _items[ i ].setSymbol(o.symbol(i) );
  }
}

#if 0
QTextStream& operator>>(QTextStream& s, Legend& p)
{
  TRACE;
  QString str;
  int r, g, b;
  bool ok;

  LineParser parser;
  parser.setString(s.readLine());
  int n=parser.value(0, &ok);
  if( !ok || !n) goto error;
  p.resize(n);
  s.readLine();
  for(int i=0; i < n; i++ ) {
    str=s.readLine();
    r=str.section( " ", 0, 0).toInt(&ok);
    if( !ok) goto error;
    g=str.section( " ", 1, 1).toInt(&ok);
    if( !ok) goto error;
    b=str.section( " ", 2, 2).toInt(&ok);
    if( !ok) goto error;
    p.pen(i).setColor(QColor( r, g, b) );

    b=str.section( " ", 3, 3).toInt(&ok);
    if( !ok) goto error;
    p.pen(i).setStyle(( Qt::PenStyle) b);
    b=str.section( " ", 4, 4).toInt(&ok);
    if( !ok) goto error;
    p.pen(i).setWidth(b * 0.295);

    r=str.section( " ", 5, 5).toInt(&ok);
    if( !ok) goto error;
    p.symbol(i).setType(( Symbol::Type) r);
    r=str.section( " ", 6, 6).toInt(&ok);
    if( !ok) goto error;
    p.symbol(i).setSize(r * 0.295);

    r=str.section( " ", 7, 7).toInt(&ok);
    if( !ok) goto error;
    g=str.section( " ", 8, 8).toInt(&ok);
    if( !ok) goto error;
    b=str.section( " ", 9, 9).toInt(&ok);
    if( !ok) goto error;
    p.symbol(i).pen().setColor(QColor( r, g, b) );

    b=str.section( " ", 10, 10).toInt(&ok);
    if( !ok) goto error;
    p.symbol(i).pen().setStyle(( Qt::PenStyle) b);
    b=str.section( " ", 11, 11).toInt(&ok);
    if( !ok) goto error;
    p.symbol(i).pen().setWidth(b * 0.295);

    r=str.section( " ", 12, 12).toInt(&ok);
    if( !ok) goto error;
    g=str.section( " ", 13, 13).toInt(&ok);
    if( !ok) goto error;
    b=str.section( " ", 14, 14).toInt(&ok);
    if( !ok) goto error;
    p.symbol(i).brush().setColor(QColor( r, g, b) );

    r=str.section( " ", 15, 15).toInt(&ok);
    if( !ok) goto error;
    p.symbol(i).brush().setStyle(( Qt::BrushStyle) r);
  }
  return s;
error:
  Message::wrongTextFormat(s, translate("Legend","Restoring legend"));
  return s;
}
#endif

void Legend::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
{
  TRACE;
  for(int i=0;i < _items.count();i++ ) _items[ i ].xml_save(s, context);
}

XMLMember Legend::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  Q_UNUSED(attributes)
  Q_UNUSED(context)
  if(tag=="LegendItem" ) {
    resize(_items.count() + 1);
    return XMLMember(&_items[ _items.count() - 1 ] );
  }
  return XMLMember(XMLMember::Unknown);
}

} // namespace SciFigs
