/* pvArrayPlugin.cpp */
/*
 * The License for this software can be found in the file LICENSE that is included with the distribution.
 */

#include <stdlib.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
#include <pv/convert.h>
#include <pv/pvSubArrayCopy.h>
#define epicsExportSharedSymbols
#include "pv/pvArrayPlugin.h"

using std::string;
using std::size_t;
using std::cout;
using std::endl;
using std::tr1::static_pointer_cast;
using std::vector;
using namespace epics::pvData;

namespace epics { namespace pvCopy{

static ConvertPtr convert = getConvert();
static std::string name("array");

PVArrayPlugin::PVArrayPlugin()
{
}

PVArrayPlugin::~PVArrayPlugin()
{
}

void PVArrayPlugin::create()
{
     static bool firstTime = true;
     if(firstTime) {
         firstTime = false;
         PVArrayPluginPtr pvPlugin = PVArrayPluginPtr(new PVArrayPlugin());
         PVPluginRegistry::registerPlugin(name,pvPlugin);
    }
}

PVFilterPtr PVArrayPlugin::create(
     const std::string & requestValue,
     const PVCopyPtr & pvCopy,
     const PVFieldPtr & master)
{
    return PVArrayFilter::create(requestValue,master);
}

PVArrayFilter::~PVArrayFilter()
{
}

static vector<string> split(string const & colonSeparatedList) {
    string::size_type numValues = 1;
    string::size_type index=0;
    while(true) {
        string::size_type pos = colonSeparatedList.find(':',index);
        if(pos==string::npos) break;
        numValues++;
        index = pos +1;
    }
    vector<string> valueList(numValues,"");
    index=0;
    for(size_t i=0; i<numValues; i++) {
        size_t pos = colonSeparatedList.find(':',index);
        string value = colonSeparatedList.substr(index,pos-index);
        valueList[i] = value;
        index = pos +1;
    }
    return valueList;
}

PVArrayFilterPtr PVArrayFilter::create(
     const std::string & requestValue,
     const PVFieldPtr & master)
{
    Type type = master->getField()->getType();
    if(type!=scalarArray) {
        PVArrayFilterPtr filter = PVArrayFilterPtr();
        return filter;
    }
    long start =0;
    long increment =1;
    long end = -1;
    vector<string> values(split(requestValue));
    long num = values.size();
    bool ok = true;
    string value;
    if(num==1) {
        value = values[0];
        start = strtol(value.c_str(),0,10);
    } else if(num==2) {
        value = values[0];
        start = strtol(value.c_str(),0,10);
        value = values[1];
        end = strtol(value.c_str(),0,10);
    } else if(num==3) {
        value = values[0];
        start = strtol(value.c_str(),0,10);
        value = values[1];
        increment = strtol(value.c_str(),0,10);
        value = values[2];
        end = strtol(value.c_str(),0,10);
    } else {
        ok = false;
    }
    if(!ok) {
        PVArrayFilterPtr filter = PVArrayFilterPtr();
        return filter;
    }
    PVArrayFilterPtr filter =
         PVArrayFilterPtr(
             new PVArrayFilter(
                 start,increment,end,static_pointer_cast<PVScalarArray>(master))); 
    return filter;
}

PVArrayFilter::PVArrayFilter(long start,long increment,long end,const PVScalarArrayPtr & masterArray)
: start(start),
  increment(increment),
  end(end),
  masterArray(masterArray)
{
}


bool PVArrayFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet,bool toCopy)
{
    PVScalarArrayPtr copyArray = static_pointer_cast<PVScalarArray>(pvCopy);
    long len = 0;
    long start = this->start;
    long end = this->end;
    long no_elements = masterArray->getLength();
    if(start<0) {
    	start = no_elements+start;
    	if(start<0) start = 0;
    }
    if (end < 0) {
    	end = no_elements + end;
    	if (end < 0) end = 0;

    }
    if(toCopy) {	
    	if (end >= no_elements) end = no_elements - 1;
    	if (end - start >= 0) len = 1 + (end - start) / increment;
    	if(len<=0 || start>=no_elements) {
    		copyArray->setLength(0);
    		return true;
    	}
    	long indfrom = start;
    	long indto = 0;
    	copyArray->setCapacity(len);
    	if(increment==1) {
            copy(*masterArray,indfrom,1,*copyArray,indto,1,len);
    	} else {
    	    for(long i=0; i<len; ++i) {
    	        copy(*masterArray,indfrom,1,*copyArray,indto,1,1);
    	        indfrom += increment;
    	         indto += 1;
    	    }
    	}
    	copyArray->setLength(len);
    	bitSet->set(pvCopy->getFieldOffset());
    	return true;
    }
    if (end - start >= 0) len = 1 + (end - start) / increment;
    if(len<=0) return true;
    if(no_elements<=end) masterArray->setLength(end+1);
    long indfrom = 0;
    long indto = start;
    if(increment==1) {
    	copy(*copyArray,indfrom,1,*masterArray,indto,1,len);
    } else {
    	for(long i=0; i<len; ++i) {
    	    copy(*copyArray,indfrom,1,*masterArray,indto,1,1);
    	    indfrom += 1;
    	     indto += increment;
    	}
    }
    return true;
}

string PVArrayFilter::getName()
{
	return name;
}

}}

