/*
 * Copyright (C) 2004 Bryant Lee
 *
 * This file is part of FPeriod.
 *
 * FPeriod 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.
 *
 * FPeriod 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 FPeriod; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

/**
 * Represents a composition of poly functions
 *
 * Written by: Bryant Lee
 * Date: 11/22/04
 **/

#include <vector>
#include <string>
#include <map> //uses a map in printDefinitions()

#include <fstream> //used in printDefinitions()

#include <math.h> //for ceil

#include "Comp.h"
#include "StringOps.h"

extern int UINTSIZE;

Comp::Comp(int iShiftSize) {
  shiftSize = iShiftSize;
}

Comp::~Comp() {
  clearFuncStore();
}

string Comp::returnName() {
  string compStr;
  int i;

  for(i = compNames.size() - 1; i >= 0; i--) {
    //elements are already trimmed (see setComposition())
    compStr.append(compNames[i]);
    if(i != 0)
      compStr.append("#");
  }

  return compStr;
}

//printDefinitions
void Comp::printDefinitions(ofstream & fout) {
  int i;
  map <string, bool> seen; //track functions used twice (so we define only once)
  map<string, Func *>::iterator frt;

  for(i = compNames.size() - 1; i >= 0; i--) {
    if(seen.find(compNames[i]) == seen.end()) {
      seen.insert(pair<string,bool>(compNames[i], true));
      frt = funcStore.find(compNames[i]);
      if(frt->second->isOpt())
	fout << "OPT ";
      fout << compNames[i] << " = ";
      frt->second->print(fout);
    }
  }
}

void Comp::print() {
  //dead function

  /*
  unsigned int size = composition.size(), i;

  for(i = 0; i < size; i++) {
    cout << "Comp func " << i << ": ";
    composition[i]->print();
  }

  cout << "\n";
  */
}

bool Comp::setComposition(const string & inStr) {
  int i;
  vector <string> cArr;

  split(inStr, '#', cArr);

  //run through string backwards to create composition
  composition.clear();
  compNames.clear();

  for(i = cArr.size() - 1; i >= 0; i--) {
    trim(cArr[i]); //permanently trims
    map<string, Func *>::iterator cfunc = funcStore.find(cArr[i]);

    if(cfunc == funcStore.end())
      return false;
    else {
      composition.push_back(cfunc->second);
      compNames.push_back(cArr[i]);
    }
  }

  return true;
}

void Comp::addFunc(const string & inStr, string name, bool opt) {
  trim(name);

  funcStore.insert(pair<string, Func *>(name,
					new Func(inStr, shiftSize, opt)));
}

void Comp::image(byte *word, int wordLength, byte *output) {
  int size = composition.size(), i;
  byte temp[wordLength];

  copyWord(temp, word, wordLength);

  for(i = 0; i < size; i++) {
    if(i % 2 == 0) {
      composition[i]->image(temp, wordLength, output);
    }
    else { // i is odd
      composition[i]->image(output, wordLength, temp);
    }
  }

  if(size % 2 == 0)
    copyWord(output, temp, wordLength);
}

/*
 * NOTE: the 4th parameter, blocks, could be derived from wordLength, but
 * it requires a division.  It is cheaper to pass it.
 */
void Comp::image(unsigned int *word, int wordLength, unsigned int *output,
		 unsigned int blocks) {
  int size = composition.size(), i;
  unsigned int temp[blocks];

  copyUIntArray(temp, word, blocks);

  for(i = 0; i < size; i++) {
    if(i % 2 == 0) {
      composition[i]->image(temp, wordLength, output, blocks);
    }
    else { // i  is odd
      composition[i]->image(output, wordLength, temp, blocks);
    }
  }

  if(size % 2 == 0)
    copyUIntArray(output, temp, blocks);
}

void Comp::clearFuncStore() {
  map<string, Func *>::iterator it;

  for(it = funcStore.begin(); it != funcStore.end(); it++) {
    delete(it->second);
  }
}











