/*
  Copyright (c) 2000 Caldera Systems

  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __kxdata_h__
#define __kxdata_h__

#include <qstring.h>
#include <qstringlist.h>
#include <qlist.h>
#include <qdict.h>
#include <qvaluelist.h>
#include <qmap.h>
#include <qsize.h>

// kxshared.h defines the KX_DECLARE_SHARED macro, which
// defines a copy constructor, a detach() method, an
// assignment operator and a destructor, for making
// all the data classes value based and implicitly
// shared
#include "kxshared.h"

#include "parser.h"

class KXDataBase
{
public:
    KXDataBase() {};
    KXDataBase &operator=( const KXDataBase & );

    void setNode( const Node::Ptr &node ) { m_node = node; }
    Node::Ptr node() const { return m_node; }

private:
    Node::Ptr m_node;
};

enum KXPointerClass { KX_PS2, KX_Serial, KX_USB, KX_Bus };

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXPointerModelPrivate : public KXShared
{
public:
    QString m_name;
    QString m_xfreeName;
    int m_type;
    KXPointerClass m_class;
    unsigned int m_buttons;
    unsigned int m_wheels;
    QString m_device;
};

class KXPointerModel : public KXDataBase
{
public:
    KXPointerModel( const QString &name, const QString &xfreeName,
                    int type,
                    KXPointerClass klass = KX_PS2, unsigned int buttons = 3,
                    unsigned int wheels = 0, const QString &device = QString::null )
        {
            d = new KXPointerModelPrivate;
            d->m_name = name;
            d->m_xfreeName = xfreeName;
            d->m_type = type;
            d->m_class = klass;
            d->m_buttons = buttons;
            d->m_wheels = wheels;
            d->m_device = device;
        }

    void setName( const QString &name ) { detach(); d->m_name = name; }
    QString name() const { chk(); return d->m_name; }

    void setXFreeName( const QString &name ) { detach(); d->m_xfreeName = name; }
    QString xfreeName() const { chk(); return d->m_xfreeName; }

    // type as defined in X11/extensions/xf86misc.h !
    void setType( int type ) { detach(); d->m_type = type; }
    int type() const { chk(); return d->m_type; }

    void setPointerClass( KXPointerClass cl ) { detach(); d->m_class = cl; }
    KXPointerClass pointerClass() const { chk(); return d->m_class; }

    void setButtons( unsigned int buttoncnt ) { detach(); d->m_buttons = buttoncnt; }
    unsigned int buttons() const { chk(); return d->m_buttons; }

    void setWheels( unsigned int wheels ) { detach(); d->m_wheels = wheels; }
    unsigned int wheels() const { chk(); return d->m_wheels; }

    void setDevice( const QString &dev ) { detach(); d->m_device = dev; }
    QString device() const { chk(); return d->m_device; }

    KX_DECLARE_SHARED2( KXPointerModel, KXPointerModelPrivate )
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXPointerDataPrivate : public KXShared
{
public:
    KXPointerDataPrivate() {}

    KXPointerModel m_model;
    QString m_port;
    bool m_emulate3rd;
};

class KXPointerData : public KXDataBase
{
public:
    KXPointerData( const KXPointerModel &model, const QString &port )
        {
            d = new KXPointerDataPrivate;
            d->m_model = model;
            d->m_emulate3rd = false;
            d->m_port = port;
        }

    void setPointerModel( const KXPointerModel &model ) { detach(); d->m_model = model; }
    KXPointerModel pointerModel() const { chk(); return d->m_model; }

    void setPort( const QString &port ) { detach(); d->m_port = port; }
    QString port() const { chk(); return d->m_port; }

    void setEmulate3rd( bool b ){ detach(); d->m_emulate3rd = b; }
    bool emulate3rd() const { chk(); return d->m_emulate3rd; }

    KX_DECLARE_SHARED2( KXPointerData, KXPointerDataPrivate )
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXKeyboardModelPrivate : public KXShared
{
public:
    QString m_name;
    QString m_id;
};

class KXKeyboardModel : public KXDataBase
{
public:
    KXKeyboardModel() {
        d = new KXKeyboardModelPrivate;
        d->m_name = QString::fromLatin1("Generic 104-key PC");
        d->m_id = QString::fromLatin1("pc104");
        d->m_kxshared_valid = true;
    }
    KXKeyboardModel(const QString& name, const QString id)
        {
            d = new KXKeyboardModelPrivate;
            d->m_name = name;
            d->m_id = id;
        }

    void setName( const QString &name ) { detach(); d->m_name = name; }
    QString name() const { chk(); return d->m_name; }

    void setId( const QString &id ) { detach(); d->m_id = id; }
    QString id() const { chk(); return d->m_id; }

    KX_DECLARE_SHARED( KXKeyboardModel, KXKeyboardModelPrivate )
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXKeyboardLayoutPrivate : public KXShared
{
public:
    QString m_name;
    QString m_id;
};

class KXKeyboardLayout : public KXDataBase
{
public:
    KXKeyboardLayout()
        {
            d = new KXKeyboardLayoutPrivate;
            d->m_name = QString::fromLatin1("U.S. English");
            d->m_id = QString::fromLatin1("us");
            d->m_kxshared_valid = true;
        }
    KXKeyboardLayout(const QString& name, const QString id)
        {
            d = new KXKeyboardLayoutPrivate;
            d->m_name = name;
            d->m_id = id;
        }

    void setName( const QString &name ) { detach(); d->m_name = name; }
    QString name() const { chk(); return d->m_name; }

    void setId( const QString &id ) { detach(); d->m_id = id; }
    QString id() const { chk(); return d->m_id; }

    KX_DECLARE_SHARED( KXKeyboardLayout, KXKeyboardLayoutPrivate )
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXKeyboardDataPrivate : public KXShared
{
public:
    KXKeyboardModel m_model;
    KXKeyboardLayout m_layout;
};

class KXKeyboardData : public KXDataBase
{
public:
    KXKeyboardData( const KXKeyboardModel &model, const KXKeyboardLayout &layout )
        { d = new KXKeyboardDataPrivate; d->m_model = model; d->m_layout = layout; }

    void setModel( const KXKeyboardModel& model ) { detach(); d->m_model = model; }
    KXKeyboardModel model() const { chk(); return d->m_model; }

    void setLayout( const KXKeyboardLayout& layout ) { detach(); d->m_layout = layout; }
    KXKeyboardLayout layout() const { chk(); return d->m_layout; }

    KX_DECLARE_SHARED2( KXKeyboardData, KXKeyboardDataPrivate )
};

struct KXModeTiming
{
    KXModeTiming() { resolution = 0; syncBegin = 0; syncEnd = 0; total = 0; }
    KXModeTiming( uint _resolution, uint _syncBegin, uint _syncEnd, uint _total )
        {
            resolution = _resolution;
            syncBegin = _syncBegin;
            syncEnd = _syncEnd;
            total = _total;
        }

    uint resolution; // number of pixels
    uint syncBegin;
    uint syncEnd;
    uint total; // htotal / vtotal
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXModeDataPrivate : public KXShared
{
public:
    QString m_name;
    float m_dotClock;
    KXModeTiming m_hTimings;
    KXModeTiming m_vTimings;
    QString m_flags;
    int m_hSkew;
    int m_vSkew;
};

class KXModeData : public KXDataBase
{
public:
    KXModeData( const QString &name, float dotClock, const KXModeTiming &hTimings,
                const KXModeTiming &vTimings, const QString &flags, int hSkew = 0,
                int vSkew = 0 )
        {
            d = new KXModeDataPrivate;
            d->m_name = name;
            d->m_dotClock = dotClock;
            d->m_hTimings = hTimings;
            d->m_vTimings = vTimings;
            d->m_flags = flags;
            d->m_hSkew = hSkew;
            d->m_vSkew = vSkew;
            d->m_kxshared_valid = true;
        }

    void setName(const QString& name) { detach(); d->m_name = name; }
    QString name() const { chk(); return d->m_name; }

    void setDotClock(float dotClock) { detach(); d->m_dotClock = dotClock; }
    float dotClock() const { chk(); return d->m_dotClock; }

    // convenience
    uint xResolution() const { return hTimings().resolution; }
    uint yResolution() const { return vTimings().resolution; }

    static float calcHFrequency( float dotClock, float total ) { return dotClock * 1000.0 / total; }
    static float calcVRefresh( float hFrequency, float total ) { return hFrequency * 1000.0 / total; }

    uint hFrequency() const { return static_cast<uint>( calcHFrequency( dotClock(), hTimings().total ) ); }
    uint vRefresh() const { return static_cast<uint>( calcVRefresh( calcHFrequency( dotClock(), hTimings().total ), vTimings().total ) + 0.5); }

    void setHTimings(const KXModeTiming& hTimings) { detach(); d->m_hTimings = hTimings; }
    KXModeTiming hTimings() const { chk(); return d->m_hTimings; }

    void setVTimings(const KXModeTiming& vTimings) { detach(); d->m_vTimings = vTimings; }
    KXModeTiming vTimings() const { chk(); return d->m_vTimings; }

    void setFlags(const QString& flags) { detach(); d->m_flags = flags; }
    QString flags() const { chk(); return d->m_flags; }

    void setHSkew(int hSkew) { detach(); d->m_hSkew = hSkew; }
    int hSkew() const { chk(); return d->m_hSkew; }

    void setVSkew(int vSkew) { detach(); d->m_vSkew = vSkew; }
    int vSkew() const { chk(); return d->m_vSkew; }

    QString modeLine() const;

    bool operator<( const KXModeData &);

    KX_DECLARE_SHARED2( KXModeData, KXModeDataPrivate )
};

typedef QValueList<KXModeData> KXModeDataList;

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXMonitorDataPrivate : public KXShared
{
public:
    QString m_model;
    QString m_vendor;
    float m_hMin, m_hMax, m_vMin, m_vMax;
    int m_size;
    QString m_hSyncStr;
    QString m_vSyncStr;
    KXModeDataList m_modes;
};

class KXMonitorData : public KXDataBase
{
public:
    KXMonitorData( const QString &vendor, const QString &model,
                   int size, float hmin, float hmax,
                   float vmin, float vmax, KXModeDataList modes )
        {
            d = new KXMonitorDataPrivate;
            d->m_model = model;
            d->m_vendor = vendor;
            d->m_size = size;
            setHSync( hmin, hmax );
            setVSync( vmin, vmax );
            d->m_modes = modes;
        }

    void setModel(const QString& model) { detach(); d->m_model = model; }
    QString model() const { chk(); return d->m_model; }

    void setVendor(const QString& vendor) { detach(); d->m_vendor = vendor; }
    QString vendor() const { chk(); return d->m_vendor; }

    void setSize( int size ) { detach(); d->m_size = size; }
    int size() const { chk(); return d->m_size; }

    void setHSync(float min, float max);
    void setVSync(float min, float max);

    float hSyncMin() const { chk(); return d->m_hMin; }
    float hSyncMax() const { chk(); return d->m_hMax; }
    float vSyncMin() const { chk(); return d->m_vMin; }
    float vSyncMax() const { chk(); return d->m_vMax; }

    QString hSyncString() const { chk(); return d->m_hSyncStr; }
    QString vSyncString() const { chk(); return d->m_vSyncStr; }

    void setModes( const KXModeDataList &modes ) { detach(); d->m_modes = modes; }
    KXModeDataList &modes() const { chk(); return d->m_modes; }

    bool operator==( const KXMonitorData &) const;

    KX_DECLARE_SHARED2( KXMonitorData, KXMonitorDataPrivate )
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXVideoCardDataPrivate : public KXShared
{
public:
    QString m_vendor;
    QString m_model;
    QString m_name;
    uint m_pciVendor;
    uint m_pciDevice;
    QString m_server;
    QString m_driver;
    int m_videoRam;
    float m_maxClock;

    bool operator==( const KXVideoCardDataPrivate &other )
        {
            return ( m_vendor == other.m_vendor ) &&
                   ( m_model == other.m_model ) &&
                   ( m_name == other.m_name ) &&
                   ( m_pciVendor == other.m_pciVendor ) &&
                   ( m_pciDevice == other.m_pciDevice ) &&
                   ( m_server == other.m_server ) &&
                   ( m_driver == other.m_driver ) &&
                   ( m_videoRam == other.m_videoRam ) &&
                   ( m_maxClock == other.m_maxClock );
        }

};

class KXVideoCardData : public KXDataBase
{
public:
    KXVideoCardData( const QString &vendor, const QString &model,
                     const QString &name, uint pciVendor,
                     uint pciDevice, const QString &server,
                     const QString &driver, int videoRam=0, int maxClock=0 )
        {
            d = new KXVideoCardDataPrivate;
            d->m_vendor = vendor;
            d->m_model = model;
            d->m_name = name;
            d->m_pciVendor = pciVendor;
            d->m_pciDevice = pciDevice;
            d->m_server = server;
            d->m_driver = driver;
            d->m_videoRam = videoRam;
            d->m_maxClock = maxClock;
        }

    void setVendor( const QString &vendor ) { detach(); d->m_vendor = vendor; }
    QString vendor() const { chk(); return d->m_vendor; }

    void setModel( const QString &model ) { detach(); d->m_model = model; }
    QString model() const { chk(); return d->m_model; }

    void setName( const QString &name ) { detach(); d->m_name = name; }
    QString name() const;

    void setPCIVendor( uint pciVendor ) { detach(); d->m_pciVendor = pciVendor; }
    uint pciVendor() const { chk(); return d->m_pciVendor; }

    void setPCIDevice( uint pciDevice ) { detach(); d->m_pciDevice = pciDevice; }
    uint pciDevice() const { chk(); return d->m_pciDevice; }

    void setServer( const QString &server ) { detach(); d->m_server = server; }
    QString server() const { chk(); return d->m_server; }

    void setDriver( const QString &driver ) { detach(); d->m_driver = driver; }
    QString driver() const { chk(); return d->m_driver; }

    //! Set video memory in kBytes
    void setVideoRam( int videoRam ) { detach(); d->m_videoRam = videoRam; }
    int videoRam() const { chk(); return d->m_videoRam; }

    //! Set maximum pixel clock in MHz
    void setMaxClock( float clock ) { detach(); d->m_maxClock = clock; }
    float maxClock() const { chk(); return d->m_maxClock; }

    bool operator==( const KXVideoCardData &other ) const { return *d == *other.d; }

    KX_DECLARE_SHARED2( KXVideoCardData, KXVideoCardDataPrivate )
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXDisplayDataPrivate : public KXShared
{
public:
    uint m_depth;
    QStringList m_modes;
    QSize m_virtualResolution;
};

class KXDisplayData : public KXDataBase
{
public:
    KXDisplayData( int depth, QStringList modes, QSize virtualResolution )
        {
            d = new KXDisplayDataPrivate;
            d->m_depth = depth;
            d->m_modes = modes;
            d->m_virtualResolution = virtualResolution;
        }

    void setDepth( uint depth ) { detach(); d->m_depth = depth; }
    uint depth() const { chk(); return d->m_depth; }

    void setModes( const QStringList &modes ) { detach(); d->m_modes = modes; }
    QStringList modes() const { chk(); return d->m_modes; }

    void setVirtualResolution( QSize virtualResolution ) { detach(); d->m_virtualResolution = virtualResolution; }
    QSize virtualResolution() const { chk(); return d->m_virtualResolution; }

    KX_DECLARE_SHARED2( KXDisplayData, KXDisplayDataPrivate )
};

typedef QValueList<KXDisplayData> KXDisplayDataList;

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXScreenDataPrivate : public KXShared
{
public:
    int m_defaultDepth;
    KXDisplayDataList m_displays;
};

class KXScreenData : public KXDataBase
{
public:
    KXScreenData( int defaultDepth, KXDisplayDataList displays )
        {
            d = new KXScreenDataPrivate;
            d->m_defaultDepth = defaultDepth;
            d->m_displays = displays;
        }

    void setDefaultDepth( int defaultDepth ) { detach(); d->m_defaultDepth = defaultDepth; }
    int defaultDepth() const { chk(); return d->m_defaultDepth; }

    void setDisplays( const KXDisplayDataList &displays ) { detach(); d->m_displays = displays; }
    KXDisplayDataList &displays() const { chk(); return d->m_displays; }

    KX_DECLARE_SHARED2( KXScreenData, KXScreenDataPrivate )
};

/**
 * Internal, do not use. This is just declared in the header for
 * performance reasons.
 */
class KXGeneralServerDataPrivate : public KXShared
{
public:
    bool m_zap;
};

class KXGeneralServerData : public KXDataBase
{
public:
    KXGeneralServerData() { d = new KXGeneralServerDataPrivate; d->m_zap = true; d->m_kxshared_valid = true; }

    // CTRL-CLT-BS shortcut
    void setZapAllowed( bool zap ) { detach(); d->m_zap = zap; }
    bool zapAllowed() const { chk(); return d->m_zap; }

    KX_DECLARE_SHARED( KXGeneralServerData, KXGeneralServerDataPrivate )
};

typedef QValueList<KXPointerModel> KXPointerModelList;
typedef QValueList<KXPointerData> KXPointerDataList;
typedef QValueList<KXKeyboardData> KXKeyboardDataList;
typedef QValueList<KXKeyboardModel> KXKeyboardModelList;
typedef QValueList<KXKeyboardLayout> KXKeyboardLayoutList;
typedef QValueList<KXVideoCardData> KXVideoCardDataList;
typedef QMap<QString, KXVideoCardDataList> KXVideoCardDataMap;
typedef QValueList<KXMonitorData> KXMonitorDataList;
typedef QMap<QString, KXMonitorDataList> KXMonitorDataMap;
typedef QValueList<KXScreenData> KXScreenDataList;

/**
 * This class holds a collection of all settings. It provides a list of
 * available settings for a certain item (monitor, video card, mouse, etc.)
 * and the currently selected settings.
 */
class KXData
{
public:
    static bool initStatic();

    static bool recreate();

    static KXData *global() { return s_global; }

    KXPointerModelList &pointerModels() { return m_pointerModels; }
    KXKeyboardModelList &keyboardModels() { return m_keyboardModels; }
    KXKeyboardLayoutList &keyboardLayouts() { return m_keyboardLayouts; }
    KXVideoCardDataMap &videoCards() { return m_videoCards; }
    KXMonitorDataMap &monitors() { return m_monitors; }
    KXGeneralServerData &generalServerData() { return m_generalServerData; }

    KXMonitorDataList &currentMonitors() { return m_currentMonitors; }
    KXKeyboardDataList &currentKeyboards() { return m_currentKeyboards; }
    KXVideoCardDataList &currentVideoCards() { return m_currentVideoCards; }
    KXPointerDataList &currentPointers() { return m_currentPointers; }
    KXScreenDataList &currentScreens() { return m_currentScreens; }
    KXModeDataList &currentModes() { return m_currentModes; }

    KXData *clone();

    void copyFrom( KXData *other );

    static QString lookupVendor( uint pciVendorID );

    static KXModeDataList vesaModes();

    static QString pointerClassToName( KXPointerClass klass );

private:
    KXData();

    bool init();

    bool loadMonitorData();
    bool loadVideoCardData();
    void loadPointerData();
    void loadKeyboardData();

    QMap<uint,QString> m_pciVendorIDMap;

    KXPointerModelList m_pointerModels;
    KXKeyboardModelList m_keyboardModels;
    KXKeyboardLayoutList m_keyboardLayouts;
    KXVideoCardDataMap m_videoCards;
    KXMonitorDataMap m_monitors;
    KXGeneralServerData m_generalServerData;

    KXMonitorDataList m_currentMonitors;
    KXKeyboardDataList m_currentKeyboards;
    KXVideoCardDataList m_currentVideoCards;
    KXPointerDataList m_currentPointers;
    KXScreenDataList m_currentScreens;
    KXModeDataList m_currentModes;

    static KXData *s_global;

    static KXModeDataList *s_vesaModes;

    static KXModeData parseModeLine( const QString &line );
};

#endif
