// OpenVanilla.h: The OpenVanilla Framework
// Copyright (c) 2004-2005 The OpenVanilla Project (http://openvanilla.org)

// The OpenVanilla Framework is released under the Artisitc License, that is,
// it is released under the same term that Perl is.

// The OpenVanilla Framework provides interface definitions for both input
// method (IM) developers and IM environments. An IM usually requires three
// inputs: the key code, a pre-editing/output buffer (for composing phonemes or 
// character parts and committing the composed output), and a "message bar"
// that displays extra information like "candidate" characters. A platform-
// dependent IM environemnt (called "Loader" in OV's own term) offers these
// objects to the loaded IM modules.

// OpenVanilla does not define anything beyond a pure abstract interface.
// It does not even define the default encoding to be used. However it is 
// recommended UTF-8 be used throughout all levels. A simple, one-way 
// conversion utility is provided as OVService::toUTF8(), and this must be
// implemented by a Loader.

// OpenVanilla makes a few assumptions about modern GUI and operating system,
// and makes minimal use of user-defined types and C++ features. For detailed
// design and its rationale, please refer to the OpenVanilla document.

// Also note that OpenVanilla recommends lazy design and fault-tolerance
// ("DWIM," or Do What I Mean, captures what is intended here, cf. Jargon File).
// Exception is not really needed here. The system should hold even if something
// is broken or comes up with unexpected output.

#ifndef __OpenVanilla_h
#define __OpenVanilla_h

#define OV_VERSION   (0x00070005)      // version 0.7.0.5 (0.7.0rc5)

// A pure base class that defines the virtual destructor
class OVBase {
public:
    virtual ~OVBase() {}
};

// Abstract interface that accesses key code and key states (e.g. if SHIFT is
// hit at the time the key code is sent)
class OVKeyCode : public OVBase  {
public:
    virtual int code()=0;
    virtual int isShift()=0;
    virtual int isCapslock()=0;    
    virtual int isCtrl()=0;
    virtual int isAlt()=0;
    virtual int isOpt() { return isAlt(); }
    virtual int isCommand() { return isAlt(); }
    virtual int isFunctionKey() { return isCtrl()||isAlt()||isOpt()||isCommand(); }
};

// Abstract interface for the pre-edit and composing buffer.
class OVBuffer : public OVBase {
public:
    virtual OVBuffer* clear()=0;
    virtual OVBuffer* append(const char *s)=0;
    virtual OVBuffer* send()=0;
    virtual OVBuffer* update()=0;
    virtual OVBuffer* update(int cursorPos, int markFrom=-1, int markTo=-1)=0;
    virtual int isEmpty()=0;
};

// Abstract interface for the message bar (called Candidate in OpenVanilla).
// By default it is a simple horizontal strip, but it is possible, for example,
// to send marked-up texts through OVCandidate::append(). The implementation
// is up to the IM environment (Loader) that provides this interface to the IM.
class OVCandidate : public OVBase  {
public:
    virtual OVCandidate* clear()=0;
    virtual OVCandidate* append(const char *s)=0;
    virtual OVCandidate* hide()=0;
    virtual OVCandidate* show()=0;
    virtual OVCandidate* update()=0;
    virtual int onScreen()=0;
};

// Abstract interface for a simple dictionary. It is recommended that this
// dictionary be implemented as a type-free dictionary, i.e. you can replace
// the key with any value of any type, and type conversion between integer and
// string is done transparently, like what is done in e.g. sqlite3.
class OVDictionary : public OVBase {
public:
    virtual int keyExist(const char *key)=0;
    virtual int getInteger(const char *key)=0;
    virtual int setInteger(const char *key, int value)=0;
    virtual const char* getString(const char *key)=0;
    virtual const char* setString(const char *key, const char *value)=0;
    virtual int getIntegerWithDefault(const char *key, int value) {
        if (!keyExist(key)) setInteger(key, value); return getInteger(key);
    }
    virtual const char* getStringWithDefault(const char *key, const char *value) {
        if (!keyExist(key)) setString(key, value); return getString(key);
    }
};

// currently supported char encoding = "big5" (short for big5-hkscs)
class OVService : public OVBase {
public:
    virtual void beep()=0;
    virtual void notify(const char *msg)=0;    
    virtual const char *locale()=0;
    virtual const char *userSpacePath(const char *modid)=0;
    virtual const char *pathSeparator()=0;

    // limited support for encoding conversion
    virtual const char *toUTF8(const char *encoding, const char *src)=0;
    virtual const char *fromUTF8(const char *encoding, const char *src)=0;
    
    // the endian order of these two functions is always that of machine order
    // (ie. on a big endian machine it's UTF16BE, otherwise UTF16LE)
    // no byte order mark (0xfffe or 0xfeff) is ever used here) 
    // also note no boundry check mechanism exists, the Loader take care about it
    virtual const char *UTF16ToUTF8(unsigned short *src, int len)=0;
    virtual int UTF8ToUTF16(const char *src, unsigned short **rcvr)=0;
};

// The input method context. The context is always created by the IM module
// (defined below) and is used exclusively by the IM environment (Loader).
// This is where your implementation for an input method goes.
// Note that the derived OVInputMethodContext::keyEvent() method MUST RETURN
// either 0 or non-zero. It should return non-zero if the event is "processed"
// by the IM context, 0 if the event is "rejected" or "not processed."
class OVInputMethodContext : public OVBase  {
public:
    virtual void start(OVBuffer*, OVCandidate*, OVService*) {}
    virtual void clear() {}
    virtual void end() {}
    virtual int keyEvent(OVKeyCode*, OVBuffer*, OVCandidate*, OVService*)=0;
};

// The common OpenVanilla module interface. Currently OpenVanilla has two
// kinds of modules, InputMethod (that creates IM context and handles
// IM configuration issues) and OutputFilter (that does any magical
// transformation/conversion of output buffer, for example, conversion
// between Traditional and Simplified Chinese, or convert all kanas into
// romanized forms, etc.)

class OVModule : public OVBase {
public:
    virtual const char *moduleType()=0;
    virtual const char *identifier()=0;
    virtual const char *localizedName(const char* locale) { return identifier(); }
    virtual int initialize(OVDictionary *moduleCfg, OVService *srv, 
        const char *modulePath) { return 1; }
    virtual void update(OVDictionary *moduleCfg, OVService *srv) {}
};

// The InputMethod module interface
class OVInputMethod : public OVModule
{
public:
    virtual OVInputMethodContext* newContext()=0;
    virtual const char* moduleType() { return "OVInputMethod"; }
};

// The OutputFilter module interface. Note we don't care in which encoding
// the source is given. It is recommended that the source be in UTF-8 and that
// the ouput be the same. Also note we don't care about the length of the 
// source, nor do we care about the length of the output. See the "lazy
// design" principle in the beginning of this header file. One more thing:
// it should be possible for the OutputFilter to return the source as output.
class OVOutputFilter : public OVModule
{
public:
    virtual const char* process(const char *source, OVService *srv)=0;
    virtual const char* moduleType() { return "OVOutputFilter"; }
};

// Common key codes. Note these are FIXED DEFINITIONS in OpenVanilla.h; an
// IM environment should map different key codes into these when offering
// OVKeyCode-derived objects to an IM context
enum 
{
    ovkEsc=27, ovkSpace=32, ovkReturn=13, 
    ovkDelete=127, ovkBackspace=8,
    ovkUp=30, ovkDown=31, ovkLeft=28, ovkRight=29,
    ovkHome=1, ovkEnd=4, ovkPageUp=11, ovkPageDown=12,
    ovkTab=9
};

#endif