/*
  $Id$

  Copyright (C) 2007 by The Regents of the University of California
	
  Redistribution of this file is permitted under
  the terms of the BSD license
    
  Date: 09/17/2007
  Author: Alexander Behm <abehm (at) ics.uci.edu>
*/

#ifndef _stringcontainer_h_
#define _stringcontainer_h_

#include "util/looptimer.h"
#include <vector>
#include <fstream>
#include <iostream>

template <class ConcreteStringContainer>
class StringContainerAbs {
public:
  // filling function common to all string containers
  void fillContainer(const char* fileName, const unsigned count, const unsigned maxLineLen = 255);      
  void fillContainer(const vector<string>& src);

  // interfaces for concrete string containers
  void retrieveString(string& target, const unsigned i) {
    static_cast<ConcreteStringContainer*>(this)->retrieveString_Impl(target, i);
  }
  
  void insertString(const string& s) {
    static_cast<ConcreteStringContainer*>(this)->insertString_Impl(s);
  }

  unsigned size() {
    return static_cast<ConcreteStringContainer*>(this)->size_Impl();
  }

  void flushCache() {
    return static_cast<ConcreteStringContainer*>(this)->flushCache_Impl();
  }
};

template <class ConcreteStringContainer>
void
StringContainerAbs<ConcreteStringContainer>::
fillContainer(const char* fileName, const unsigned count, const unsigned maxLineLen) {
  ifstream fileData(fileName);
  if (!fileData) {
    cout << "can't open input file \"" << fileName << "\"" << endl;
    return;
  }
  
  cout << "INPUTFILE: \"" << fileName << "\"" << endl;
  
  char line[maxLineLen + 1];
  bool isIgnore = false;
  
  LoopTimer loopTimer;
  loopTimer.begin("FILLING CONTAINER", count);
  while (true) {
    fileData.getline(line, maxLineLen + 1);
    if (fileData.eof())
      break;
    if (fileData.rdstate() & ios::failbit) {
      isIgnore = true;
      while (fileData.rdstate() & ios::failbit) {      
	fileData.clear(fileData.rdstate() & ~ios::failbit);
	fileData.getline(line, maxLineLen);
      }
      cout << "open reading input file \"" << fileName << "\"" << endl
	   << "line length might exceed " << maxLineLen << " characters" << endl;
      return;
    }
    else
      insertString(string(line));
    if (count != 0 && this->size() == count)
      break;
    loopTimer.next();
  }
  loopTimer.end();

  fileData.close();
  
  if (isIgnore)
    cout << "WARNING" << endl 
	 << "some lines in the file exceeded " << maxLineLen 
	 << " characters and were ignored" << endl;

  flushCache();
}

template <class ConcreteStringContainer>
void
StringContainerAbs<ConcreteStringContainer>::
fillContainer(const vector<string>& src) {
  for(unsigned i = 0; i < src.size(); i++) insertString(src.at(i));
  flushCache();
}


// very simple string container which is just a wrapper around stl vector
class StringContainerVector : public StringContainerAbs<StringContainerVector> {
private:
  vector<string> container;
public:  
  void retrieveString_Impl(string& target, const unsigned i) { target = container.at(i); }
  void insertString_Impl(const string& s) { container.push_back(s); }
  unsigned size_Impl() { return container.size(); }
  void flushCache_Impl() {}
};

#endif
