/*
 * See the README file for copyright information and how to reach the author.
 *
 */

#include "common.h"

class cRMDeviceItem
{
    public:
        eSetupItem item;
        const char* name;
};

static const eSetupItem criticalItems[]=
{
    eSetupAudioBitrate,
    eSetupVideoBitrateAverage,
    eSetupVideoBitratePeak,
    eSetupVideoStandard,
    eSetupVbr,
    eSetupResolutionHorizontal,
    eSetupResolutionVertical
};

/* all updateable values for a device */
static const cRMDeviceItem deviceItemList[]=
{
    { eSetupVolume, "ctl_volume" },
    { eSetupAudioBitrate, "ctl_audio_bitrate" },
    { eSetupBrightness, "ctl_brightness" },
    { eSetupContrast, "ctl_contrast" },
    { eSetupHue, "ctl_hue" },
    { eSetupSaturation, "ctl_saturation" },
    { eSetupVideoBitrateAverage, "ctl_video_bitrate" },
    { eSetupVideoBitratePeak, "ctl_video_bitrate_peak" },
    { eSetupVbr, "ctl_video_bitrate_mode" },
    { eSetupVideoStandard, "ctl_video_standard" },
    { eSetupVideoInput, "ctl_input" },
    { eSetupResolutionHorizontal, "ctl_resolution_hor" },
    { eSetupResolutionVertical, "ctl_resolution_ver" },
    { eSetupFrequency, "ctl_frequency" },
    { eSetupSignalPresent, "ctl_signal_present" }
};

cPvrUsb2Device::cPvrUsb2Device(int _index, char* _id):
    cRMDevice(_index)
{
    videoDeviceId=-1;
    videoDeviceName=strdup("/dev/video0");
    videoControlName=NULL;

    asprintf(&videoControlName, "%s/%s", DeviceBase(), _id);
    
    int numItems=NumItems();
    eSetupItem maxItem=(eSetupItem)0;

    for(int i=0; i<numItems; i++) {
        if(deviceItemList[i].item>maxItem) {
            maxItem=deviceItemList[i].item;
        }
    }
    itemArraySize=maxItem+1;
    deviceItems=new cRMDeviceItem[itemArraySize]; 
    memset(deviceItems, 0, sizeof(cRMDeviceItem)*itemArraySize);
    for(int i=0; i<numItems; i++) {
        eSetupItem index=deviceItemList[i].item;
        deviceItems[index]=deviceItemList[i];
    }

    cPvrUsb2Plugin::LogDebug("Create Instance: %s", _id);
}

cPvrUsb2Device::~cPvrUsb2Device()
{
    if(videoDeviceName) {
        free(videoDeviceName);
        videoDeviceName=NULL;
    }
    if(videoControlName) {
        free(videoControlName);
        videoControlName=NULL;
    }

    delete[] deviceItems;
    deviceItems=NULL;
}

int cPvrUsb2Device::NumItems()
{
    return ENTRIES(deviceItemList, cRMDeviceItem);
}

eSetupItem cPvrUsb2Device::Item(int _iterIndex)
{
    if(_iterIndex<0 || _iterIndex>=NumItems()) {
        return (eSetupItem)-1;
    }
    return deviceItemList[_iterIndex].item;
}

bool cPvrUsb2Device::IsValidItem(eSetupItem _item) const
{
    if(_item<0 || _item>itemArraySize ||
            !deviceItems || !deviceItems[_item].name)
    {
        return false;
    }
    return true;
}

const char* cPvrUsb2Device::ItemName(eSetupItem _item) const
{
    if(!IsValidItem(_item)) {
        return NULL;
    }
    return deviceItems[_item].name;
}

bool cPvrUsb2Device::IsCriticalItem(eSetupItem _item)
{
    for(int i=0; i<ENTRIES(criticalItems, eSetupItem); i++) {
        if(criticalItems[i]==_item) {
            return true;
        }
    }
    return false;
}

const char* cPvrUsb2Device::ControlName(eControlValue _type) const
{
    switch(_type) {
        case eControlCurrentValue:
            return "cur_val";
        case eControlMinValue:
            return "min_val";
        case eControlMaxValue:
            return "max_val";
    }
    return NULL;
}

char* cPvrUsb2Device::ReadValue(const char* _path) const
{
    FILE* stream=fopen(_path, "r");
    if(!stream) {
        return NULL;
    }
    cReadLine line;
    char* value=line.Read(stream);
    fclose(stream);
    if(!value) {
        return NULL;
    }
    return strdup(value);
}

char* cPvrUsb2Device::StringValue(eSetupItem _item, eControlValue _type) const
{
    const char* name=ItemName(_item);
    if(!name) {
        return NULL; 
    }
    const char* type=ControlName(_type);
    if(!type) {
        return NULL; 
    }

    char path[256];
    snprintf(path, sizeof(path), "%s/%s/%s", videoControlName, name, type);
    return ReadValue(path);
}

int cPvrUsb2Device::Value(eSetupItem _item, eControlValue _type) const
{
    char* value=StringValue(_item, _type);
    if(!value) {
        return 0;
    }
    int retVal=atoi(value);
    free(value);
    switch(_item) {
        case eSetupAudioBitrate:
            retVal*=1000;
            break;
        case eSetupFrequency:
            retVal/=1000;
            break;
        default:
            break;
    }
    return retVal;
}

int cPvrUsb2Device::CurrentValue(eSetupItem _item) const
{
    return Value(_item, eControlCurrentValue);
}

int cPvrUsb2Device::MinValue(eSetupItem _item) const
{
    return Value(_item, eControlMinValue);
}

int cPvrUsb2Device::MaxValue(eSetupItem _item) const
{
    return Value(_item, eControlMaxValue);
}

bool cPvrUsb2Device::SetValue(eSetupItem _item, int _value)
{
    char* value;
    switch(_item) {
        case eSetupAudioBitrate:
            asprintf(&value, "%dkb/s", _value/1000);
            break;
        case eSetupFrequency:
            asprintf(&value, "%d", _value*1000);
            break;
        case eSetupVideoStandard:
            asprintf(&value, "%s", 
                    cRMSetupTools::Index2StringValue(eVideoStandardItem, _value));
            break;
        default:
            asprintf(&value, "%d", _value);
            break;
    }
    int retVal=SetValue(_item, value);
    free(value);
    return retVal;
}

bool cPvrUsb2Device::SetValue(eSetupItem _item, const char* _value)
{
    const char* name=ItemName(_item);
    if(!name) {
        return false; 
    }

    char* current=StringValue(_item, eControlCurrentValue);
    if(strcmp(current, _value)==0) {
//        cPvrUsb2Plugin::LogDebug("SetValue: no change %s: %s", name, current);
        free(current);
        return true;
    }
    cPvrUsb2Plugin::LogDebug("SetValue: change %s: %s=>%s", name,
            _value, current);
    free(current);

    if(IsCriticalItem(_item)) {
        CloseVideoDevice();
    }

    char* path;
    asprintf(&path, "%s/%s/%s", videoControlName, name,
            ControlName(eControlCurrentValue));
    FILE* stream=fopen(path, "w");
    free(path);
    if(!stream) {
        return false;
    }
    bool retVal=true;
    if(fprintf(stream, "%s\n", _value)<0) {
        retVal=false;
    }
    fclose(stream);
    return true;
}

int cPvrUsb2Device::VideoDevice()
{
    if(videoDeviceId<0) {
        char path[256];
        snprintf(path, sizeof(path), "%s/%s", videoControlName, "v4l_minor_number");
        char* value=ReadValue(path);
        if(value) {
            cPvrUsb2Plugin::LogDebug("Found video device: %s", value);
            videoDeviceId=atoi(value);
            free(value);
        }
    }
    return videoDeviceId;
}

bool cPvrUsb2Device::SetVideoDevice(int _device)
{
    if(videoDeviceId!=_device) {
        StopReader(); 
    }
    videoDeviceId=_device;
    if(videoDeviceId>=0) {
        if(videoDeviceName) {
            free(videoDeviceName);    
        }
        asprintf(&videoDeviceName, "/dev/video%d", videoDeviceId);
    }
    cPvrUsb2Plugin::LogDebug("Set video device: %s(%d)", videoDeviceName, videoDeviceId);
    if(!videoDeviceName) {
        return false; 
    }
    return true;
}
