#ifndef FACT_PixelMap
#define FACT_PixelMap

#include<vector>
#include<string>
#include<fstream>
#include<sstream>

#include "tools.h"

#ifdef DEBUG
#include<iostream>  // cerr -- to be removed?
#endif

// FIXME: Replace 416 by BIAS::kNumChannels

struct PixelMapEntry
{
    int   index;               /// Software index
    int   cbpx;                /// Hardware index as CBPX
    int   gapd;                /// gAPD index
//    float Vgapd;               /// gAPD Bias voltage
    int   hv_board;            /// Bias suppply board
    int   hv_channel;          /// Bias supply channel

    int crate() const { return cbpx/1000; }
    int board() const { return (cbpx/100)%10; }
    int patch() const { return (cbpx/10)%10; }
    int pixel() const { return cbpx%10; }
    int hw() const    { return pixel()+patch()*9+board()*36+crate()*360; }
    int group() const { return pixel()>3; }
    int hv() const    { return hv_channel+hv_board*32; }

    operator bool() const { return index>=0; }
};

class PixelMap : public std::vector<PixelMapEntry>
{
public:
    static const PixelMapEntry empty;

    PixelMap() : std::vector<PixelMapEntry>(1440)
    {
    }

    bool Read(const std::string &fname)
    {
        std::ifstream fin(fname);

        int l = 0;

        std::string buf;
        while (getline(fin, buf, '\n'))
        {
            if (l>1439)
                break;

            buf = Tools::Trim(buf);
            if (buf[0]=='#')
                continue;

            std::stringstream str(buf);

            int     idummy;
            float   fdummy;

            PixelMapEntry entry;

            str >> entry.index;
            str >> entry.cbpx;
            str >> idummy;
            str >> idummy;
            str >> entry.gapd;
            str >> fdummy; //entry.Vgapd;
            str >> entry.hv_board;
            str >> entry.hv_channel;
            //str >> fdummy;
            //str >> fdummy;
            //str >> fdummy;

            if (entry.hv_channel+32*entry.hv_board>=416/*BIAS::kNumChannels*/)
            {
#ifdef DEBUG
                cerr << "Invalid board/channel read from FACTmapV5.txt." << endl;
#endif
                return false;
            }

            (*this)[l++] = entry;
        }

        return l==1440;
    }

    const PixelMapEntry &index(int idx) const
    {
        for (std::vector<PixelMapEntry>::const_iterator it=begin(); it!=end(); it++)
            if (it->index==idx)
                return *it;
#ifdef DEBUG
        std::cerr << "PixelMap: index " << idx << " not found" << std::endl;
#endif
        return empty;
    }

    const PixelMapEntry &cbpx(int c) const
    {
        for (std::vector<PixelMapEntry>::const_iterator it=begin(); it!=end(); it++)
            if (it->cbpx==c)
                return *it;
#ifdef DEBUG
        std::cerr << "PixelMap: cbpx " << c << " not found" << std::endl;
#endif
        return empty;
    }

    const PixelMapEntry &cbpx(int c, int b, int p, int px) const
    {
        return cbpx(px + p*10 + b*100 + c*1000);
    }

    const PixelMapEntry &hw(int idx) const
    {
        return cbpx(idx/360, (idx/36)%10, (idx/9)%4, idx%9);
    }

    const PixelMapEntry &hv(int board, int channel) const
    {
        for (std::vector<PixelMapEntry>::const_iterator it=begin(); it!=end(); it++)
            if (it->hv_board==board && it->hv_channel==channel)
                return *it;
#ifdef DEBUG
        std::cerr << "PixelMap: hv " << board << "/" << channel << " not found" << std::endl;
#endif
        return empty;
    }

    const PixelMapEntry &hv(int idx) const
    {
        return hv(idx/32, idx%32);
    }

    /*
    float Vgapd(int board, int channel) const
    {
        float avg = 0;
        int   num = 0;

        for (std::vector<PixelMapEntry>::const_iterator it=begin(); it!=end(); it++)
            if (it->hv_board==board && it->hv_channel==channel)
            {
                avg += it->Vgapd;
                num ++;
            }

        return num==0 ? 0 : avg/num;
    }

    float Vgapd(int idx) const
    {
        return Vgapd(idx/32, idx%32);
    }

    std::vector<float> Vgapd() const
    {
        std::vector<float> avg(416);
        std::vector<int>   num(416);

        for (std::vector<PixelMapEntry>::const_iterator it=begin(); it!=end(); it++)
        {
            const int ch = it->hv_board*32 + it->hv_channel;

            avg[ch] += it->Vgapd;
            num[ch] ++;
        }

        for (int ch=0; ch<416; ch++)
        {
            if (num[ch])
                avg[ch] /= num[ch];
        }

        return avg;
    }*/
};

struct BiasMapEntry
{
    int   hv_board;            /// Bias suppply board
    int   hv_channel;          /// Bias supply channel
    float Vnom;                /// Channel bias voltage nominal
    float Voff;                /// Channel bias voltage offset

    int hv() const { return hv_channel+hv_board*32; }
};

class BiasMap : public std::vector<BiasMapEntry>
{
public:
    static const BiasMapEntry empty;

    BiasMap() : std::vector<BiasMapEntry>(416)
    {
    }

    bool Read(const std::string &fname)
    {
        std::ifstream fin(fname);

        int l = 0;

        std::string buf;
        while (getline(fin, buf, '\n'))
        {
            if (l>416)
                break;

            buf = Tools::Trim(buf);
            if (buf[0]=='#')
                continue;

            std::stringstream str(buf);

            BiasMapEntry entry;

            str >> entry.hv_board;
            str >> entry.hv_channel;
            str >> entry.Vnom;
            str >> entry.Voff;

            if (entry.hv_channel+32*entry.hv_board>=416)
            {
#ifdef DEBUG
                cerr << "Invalid board/channel read from " << fname << "." << endl;
#endif
                return false;
            }

            (*this)[l++] = entry;
        }

        return l==416;
    }

    const BiasMapEntry &hv(int board, int channel) const
    {
        for (std::vector<BiasMapEntry>::const_iterator it=begin(); it!=end(); it++)
            if (it->hv_board==board && it->hv_channel==channel)
                return *it;
#ifdef DEBUG
        std::cerr << "PixelMap: hv " << board << "/" << channel << " not found" << std::endl;
#endif
        return empty;
    }

    const BiasMapEntry &hv(int idx) const
    {
        return hv(idx/32, idx%32);
    }

    float Vgapd(int board, int channel) const
    {
        const BiasMapEntry &entry = hv(board, channel);
        return entry.Vnom+entry.Voff;
    }

    float Vgapd(int idx) const
    {
        return Vgapd(idx/32, idx%32);
    }

    std::vector<float> Vgapd() const
    {
        std::vector<float> volt(416);

        for (std::vector<BiasMapEntry>::const_iterator it=begin(); it!=end(); it++)
        {
            const int ch = it->hv_board*32 + it->hv_channel;
            volt[ch] += it->Vnom+it->Voff;
        }

        return volt;
    }
};

#endif
