#include "import.h"
#include "util/gzstream.h"

using namespace std;
//---------------------------------------------------------------------------
std::string ImportLang::bareName (const std::string& fileName) {
    size_t kdot = fileName.find_last_of (suffixDelim);		// size_t: schmidt
    size_t kslash = fileName.find_last_of (dirDelim) +1;
    if (kdot == kslash) kdot = fileName.npos;				// special for .xxxx files
    return fileName.substr (kslash, kdot-kslash);
}

// ===========================================================
ImportLang::TRegistry ImportLang::registry_;
// -----------------------------------------------------------
ImportLang* ImportLang::byName (const std::string& sought) {    // select ImportLang by name
    for (TRegistry::const_iterator lg = registry_.begin(); lg != registry_.end(); lg++) {
        if (sought == (*lg)->name()) return (*lg);
    }
    return NULL;
}
// -----------------------------------------------------------
ImportLang* ImportLang::bySuffix (const std::string& sought) {  // select ImportLang by suffix
    for (TRegistry::const_iterator lg = registry_.begin(); lg != registry_.end(); lg++) {
        if ((*lg)->hasSuffix(sought)) return (*lg);
    }
    return NULL;
}
// ------------------------------------------------------------
ImportLang* ImportLang::addSuffix (const std::string& added) { 
    suffixes_.push_back (added);
    return this;
}
// ------------------------------------------------------------
bool ImportLang::hasSuffix (const std::string& sought) const {
    for (vector <string>::const_iterator sf = suffixes_.begin(); sf != suffixes_.end(); sf++) {
        if ((*sf) == sought) return true;
    }
    return false;
}

// ------------------------------------------------------------
ImportLang::ImportLang (const std::string& n): name_(n) {}
// ===========================================================
ImportStream::TRegistry ImportStream::registry_;
ImportStream* ImportStream::default_;
// -----------------------------------------------------------
ImportStream* ImportStream::byName (const std::string& sought) {    // select ImportStream by name
    for (TRegistry::const_iterator lg = registry_.begin(); lg != registry_.end(); lg++) {
        if (sought == (*lg)->name()) return (*lg);
    }
    return NULL;
}
// -----------------------------------------------------------
ImportStream* ImportStream::bySuffix (const std::string& sought) {  // select ImportStream by suffix
    for (TRegistry::const_iterator lg = registry_.begin(); lg != registry_.end(); lg++) {
        if ((*lg)->hasSuffix(sought)) return (*lg);
    }
    return NULL;
}
// ------------------------------------------------------------
ImportStream* ImportStream::byExistence (std::string& sought) {    // select existing stream and return the real name
    std::string realname;
    for (TRegistry::const_iterator lg = registry_.begin(); lg != registry_.end(); lg++) {
        realname = (*lg)->hasFile (sought);
        if (!realname.empty()) {
            sought = realname;
            return (*lg);
        }
    }
    return NULL;
}
// ------------------------------------------------------------
std::string ImportStream::hasFile (const std::string& sought) {
    std::istream* istr;
    for (vector <string>::const_iterator sf = suffixes_.begin(); sf != suffixes_.end(); sf++) {
        istr = getStream (sought+(*sf));
        if (istr->good()) {
            closeStream (istr);
            delete istr;
            return sought+(*sf);
        }
    }
    return NULL;
}
// ------------------------------------------------------------
ImportStream* ImportStream::addSuffix (const std::string& added) { 
    suffixes_.push_back (added);
    return this;
}
// ------------------------------------------------------------
bool ImportStream::hasSuffix (const std::string& sought) const {
    for (vector <string>::const_iterator sf = suffixes_.begin(); sf != suffixes_.end(); sf++) {
        if ((*sf) == sought) return true;
    }
    return false;
}
// ------------------------------------------------------------
ImportStream::ImportStream (const std::string& n): name_(n) {}

// ============================================================
GzImportStream::GzImportStream (const std::string& n): ImportStream(n) {}

// ------------------------------------------------------------
void GzImportStream::enregister (void) {
    registry_.push_back((new GzImportStream ("GZIP"))->addSuffix (".gz"));
}
// ------------------------------------------------------------
std::istream* GzImportStream::getStream (void) {
    MeteredBuf* gzbuf = new MeteredBuf;
    std::istream* is = new istream (gzbuf);
    return is;
}
// ------------------------------------------------------------
std::istream* GzImportStream::getStream (const std::string& n, msg::Talker* t) {
    MeteredBuf* gzbuf = new MeteredBuf;
    gzbuf->open (n, t);
    std::istream* is = new istream (gzbuf);
    return is;
}
// ------------------------------------------------------------
void GzImportStream::openStream (std::istream* s, const std::string& n, msg::Talker* t) {
    MeteredBuf* gzbuf = dynamic_cast<MeteredBuf*> (s->rdbuf());
    gzbuf->open (n, t);
}
// ------------------------------------------------------------
void GzImportStream::closeStream (std::istream* s) {
    gzstreambuf* gzbuf = dynamic_cast<gzstreambuf*> (s->rdbuf());
    gzbuf->close ();
}
// ============================================================
bool GzImportStream::MeteredBuf::open (const std::string& name, msg::Talker* t) {
    talker = t;
    if (!gzstreambuf::open (name.c_str(), std::ios::in)) return false;
    if (talker) {
        count=0;
        gmsg.gaugeType (GaugeMsg::tick);
        gmsg.range (1);
        gmsg.progress (0);
        talker->broadcast (&gmsg);
    }
    return true;
}
// ------------------------------------------------------------
int GzImportStream::MeteredBuf::underflow() {
    if (talker) {
        count++;
        if ((count & 0x7) == 0) {
            talker->broadcast (&gmsg);
        }
    }
    return gzstreambuf::underflow();
}
// ------------------------------------------------------------
int GzImportStream::MeteredBuf::uflow() {
    if (talker) {
        count++;
        if ((count & 0x7) == 0) {
            talker->broadcast (&gmsg);
        }
    }
    return gzstreambuf::uflow();
}
// ============================================================
PlainImportStream::PlainImportStream (const std::string& n): ImportStream(n) {}

// ------------------------------------------------------------
void PlainImportStream::enregister (void) {
    default_ = new PlainImportStream ("none");
    registry_.push_back(default_);
}
// ------------------------------------------------------------
std::istream* PlainImportStream::getStream (void) {
    return new std::istream(new MeteredBuf);
}
// ------------------------------------------------------------
std::istream* PlainImportStream::getStream (const std::string& n, msg::Talker* t) {
    MeteredBuf* buf = new MeteredBuf;
    std::istream* s = new std::istream (buf);
    if (!buf->open (n, t)) s->setstate (std::ios::failbit);
    return s;
}
// ------------------------------------------------------------
void PlainImportStream::openStream (std::istream* s, const std::string& n, msg::Talker* t) {
    MeteredBuf* buf = dynamic_cast <MeteredBuf*> (s->rdbuf());
    if (!buf->open (n, t)) s->setstate (std::ios::failbit);
}
// ------------------------------------------------------------
void PlainImportStream::closeStream (std::istream* s) {
    s->clear();
    MeteredBuf* buf = dynamic_cast <MeteredBuf*> (s->rdbuf());
    buf->close();
}
// ============================================================
bool PlainImportStream::MeteredBuf::open (const std::string& name, msg::Talker* t) {
    talker = t;
    if (!std::filebuf::open (name.c_str(), std::ios::in)) return false;
    if (talker) {
        gmsg.gaugeType (GaugeMsg::normal);
        pubseekoff (0, std::ios::end, std::ios::in);
        std::streambuf::pos_type fileend = pubseekoff (0, std::ios::cur, std::ios::in);
        pubseekoff (0, std::ios::beg, std::ios::in);
        gmsg.range ((size_t)fileend);
        gmsg.progress (0);
        talker->broadcast (&gmsg);
    }
    return true;
}
// ------------------------------------------------------------
int PlainImportStream::MeteredBuf::underflow() {
    if (talker) {
        std::streambuf::pos_type cur = pubseekoff (0, std::ios::cur, std::ios::in);
        gmsg.progress (cur);
        talker->broadcast (&gmsg);
    }
    return std::filebuf::underflow();
}
// ------------------------------------------------------------
int PlainImportStream::MeteredBuf::uflow() {
    if (talker) {
        std::streambuf::pos_type cur = pubseekoff (0, std::ios::cur, std::ios::in);
        gmsg.progress (cur);
        talker->broadcast (&gmsg);
    }
    return std::filebuf::uflow();
}

// ============================================================
ImportCtx::ImportCtx (const std::string& lang): lang_(lang) {}
ImportCtx::ImportCtx (const ImportCtx& that): lang_(that.lang_) {}
ImportCtx::ImportCtx (int): lang_("?") {}
std::string ImportCtx::tell (void) const { return "import from "+lang_; }
