/***************************************************************************
 *   Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org>                  *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/
#ifndef PROG_XML_TO_DATA_H
#define PROG_XML_TO_DATA_H

#include <tqfile.h>
#include <tqtextstream.h>
#include <tqmap.h>

#include "xml_to_data.h"
#include "devices/list/device_list.h"

//----------------------------------------------------------------------------
template <class Data>
class ExtXmlToData : public ::XmlToData
{
public:
  ExtXmlToData(const TQString &basename, const TQString &namespac)
    : _basename(basename), _namespace(namespac) {}

protected:
  TQString _basename, _namespace;
  virtual bool hasFamilies() const { return true; }
  const TQStringList &families() const { return _families; }
  virtual uint familyIndex(const TQString &family) const { return _families.findIndex(family); }
  virtual void parseData(TQDomElement, Data &) = 0;
  virtual void includes(TQTextStream &) const {}
  virtual void outputData(const Data &, TQTextStream &) const {}
  virtual void outputFunctions(TQTextStream &) const {}
  virtual TQString currentDevice() const { return _current; }
  virtual void parseDevice(TQDomElement element);
  ::Group::Support extractSupport(const TQString &s) const;
  bool hasDevice(const TQString &device) const { return _map.contains(device); }
  virtual void parse();

protected:
  TQString _current;
  class PData {
  public:
    uint family;
    ::Group::Support support;
    Data data;
  };
  TQMap<TQString, PData> _map;
  TQStringList         _families;

  virtual void output();
};

template <class Data>
Group::Support ExtXmlToData<Data>::extractSupport(const TQString &s) const
{
  if ( s.isEmpty() ) return Group::Support::Untested;
  Group::Support support = Group::Support::fromKey(s);
  if ( support==Group::Support::None ) tqFatal("Cannot be \"not supported\"");
  if ( support==Group::Support::Nb_Types ) tqFatal("Unknown support type");
  return support;
}

template <class Data>
void ExtXmlToData<Data>::parseDevice(TQDomElement element)
{
  if ( element.nodeName()!="device" ) tqFatal("Root node child should be named \"device\"");
  _current = element.attribute("name").upper();
  if ( Device::lister().data(_current)==0 ) tqFatal(TQString("Device name \"%1\" unknown").arg(_current));
  if ( _map.contains(_current) ) tqFatal(TQString("Device \"%1\" already parsed").arg(_current));
  PData data;
  if ( hasFamilies() ) {
    TQString family = element.attribute("family");
    if ( family.isEmpty() ) tqFatal(TQString("Family is empty").arg(family));
    if ( _families.find(family)==_families.end() ) _families.append(family);
    data.family = familyIndex(family);
  }
  data.support = extractSupport(element.attribute("support_type"));
  parseData(element, data.data);
  _map[_current] = data;
}

template <class Data>
void ExtXmlToData<Data>::parse()
{
  TQDomDocument doc = parseFile(_basename + ".xml");
  TQDomElement root = doc.documentElement();
  if ( root.nodeName()!="type" ) tqFatal("Root node should be \"type\"");
  if ( root.attribute("name")!=_basename ) tqFatal(TQString("Root node name is not \"%1\"").arg(_basename));
  TQDomNode child = root.firstChild();
  while ( !child.isNull() ) {
    if ( child.isComment() ) tqDebug("comment: %s", child.toComment().data().latin1());
    else {
      if ( !child.isElement() ) tqFatal("Root node child should be an element");
      parseDevice(child.toElement());
    }
    child = child.nextSibling();
  }
}

template <class Data>
void ExtXmlToData<Data>::output()
{
  // write .cpp file
  TQFile file(_basename + "_data.cpp");
  if ( !file.open(IO_WriteOnly) ) tqFatal(TQString("Cannot open output file \"%1\"").arg(file.name()));
  TQTextStream s(&file);
  s << "// #### Do not edit: this file is autogenerated !!!" << endl << endl;
  s << "#include \"devices/list/device_list.h\"" << endl;
  s << "#include \"" + _basename + ".h\"" << endl;
  s << "#include \"" + _basename + "_data.h\"" << endl;
  includes(s);
  s << endl;
  s << "namespace " << _namespace << endl;
  s << "{" << endl;
  s << "struct CData {" << endl;
  s << "  const char *name;" << endl;
  if ( hasFamilies() ) s << "  uint family;" << endl;
  s << "  uint support;" << endl;
  s << "  Data data;" << endl;
  s << "};" << endl;
  s << endl;

  // data list
  typename TQMap<TQString, PData>::const_iterator it = _map.begin();
  for (; it!=_map.end(); ++it) {
    s << "const CData PIC" << it.key() << "_DATA = {";
    s << "  \"" << it.key() << "\", ";
    if ( hasFamilies() ) s << it.data().family << ", ";
    s << it.data().support.type() << ", ";
    s << "{ ";
    outputData(it.data().data, s);
    s << " }";
    s << " };" << endl;
  }
  s << endl;
  s << "const CData *DATA_LIST[] = {" << endl;
  uint i = 0;
  it = _map.begin();
  for (; it!=_map.end(); ++it) {
    s << "&PIC" << it.key() << "_DATA,";
    i++;
    if ( (i%10)==0 ) s << endl;
  }
  s << "0 " << endl;
  s << "};" << endl;

  // functions
  s << endl;
  s << "const CData *cdata(const TQString &device)" << endl;
  s << "{" << endl;
  s << "  for(uint i=0; DATA_LIST[i]; i++)" << endl;
  s << "    if ( device==DATA_LIST[i]->name ) return DATA_LIST[i];" << endl;
  s << "  return 0;" << endl;
  s << "}" << endl;
  s << "bool isSupported(const TQString &device)" << endl;
  s << "{" << endl;
  s << "  return cdata(device);" << endl;
  s << "}" << endl;
  if ( hasFamilies() ) {
    s << "uint family(const TQString &device)" << endl;
    s << "{" << endl;
    s << "  return cdata(device)->family;" << endl;
    s << "}" << endl;
  }
  s << "::Group::Support support(const TQString &device)" << endl;
  s << "{" << endl;
  s << "  return ::Group::Support::Type(cdata(device)->support);" << endl;
  s << "}" << endl;
  s << "const Data &data(const TQString &device)" << endl;
  s << "{" << endl;
  s << "  return cdata(device)->data;" << endl;
  s << "}" << endl;
  s << endl;
  outputFunctions(s);
  s << endl;
  s << "}" << endl;
}

//----------------------------------------------------------------------------
namespace Programmer
{
template <class Data>
class XmlToData : public ExtXmlToData<Data>
{
public:
  XmlToData(const TQString &basename, const TQString &namespac)
    : ExtXmlToData<Data>(basename, namespac) {}

protected:
  virtual void outputFunctions(TQTextStream &s) const;
  virtual void includes(TQTextStream &) const;
};

template <class Data>
void Programmer::XmlToData<Data>::outputFunctions(TQTextStream &s) const
{
  s << "void Group::initSupported()" << endl;
  s << "{" << endl;
  s << "  for (uint i=0; DATA_LIST[i]; i++) {" << endl;
  s << "    const Device::Data *data = Device::lister().data(DATA_LIST[i]->name);" << endl;
  s << "    addDevice(data->name(), data, ::Group::Support::Type(DATA_LIST[i]->support));" << endl;
  s << "  }" << endl;
  s << "}" << endl;
  s << endl;
}

template <class Data>
void Programmer::XmlToData<Data>::includes(TQTextStream &s) const
{
  s << "#include \"" << ExtXmlToData<Data>::_basename << "_prog.h\"" << endl;
}

} // namespace

#endif
