function varargout = SMSeriesGUI(varargin)
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @SMSeriesGUI_OpeningFcn, ...
                   'gui_OutputFcn',  @SMSeriesGUI_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end

function DeactiveCtr(handle)

handle.btnInitializing.Enable = 'off';
handle.popChannelList.Enable = 'off';
handle.edIntTime.Enable = 'off';
handle.edAvgTime.Enable = 'off';
handle.radInternalTrg.Enable = 'off';
handle.radExternalTrg.Enable = 'off';
handle.radFreeRunPre.Enable = 'off';
handle.radFreeRunNext.Enable = 'off';
handle.radSWTrigger.Enable = 'off';
handle.radFalling.Enable = 'off';
handle.radRising.Enable = 'off';
handle.btnPixel.Enable = 'off';
handle.btnFactory.Enable = 'off';
handle.btnPixel.Enable = 'off';
handle.btnUserCal.Enable = 'off';
handle.btnGetData.Enable = 'off';
handle.btnSave.Enable = 'off';

function SMSeriesGUI_OpeningFcn(hObject, eventdata, handles, varargin)

global SPFunc
global axXlabel

DeactiveCtr(handles)

SPFunc = SPETHlibrary();
SPFunc.LoadLibrary() %This function is load a sp dll from MATLAB

handles.popChannelList.String = strings(1,10);
handles.edIPAddress.String = "192.168.1.";

axXlabel = 'Pixel';

xlabel(axXlabel);
ylabel('Intensity(a.u)');

handles.output = hObject;
guidata(hObject, handles);

function varargout = SMSeriesGUI_OutputFcn(~, ~, handles) 

varargout{1} = handles.output;

function btnCheckConnection_Callback(~, ~, handles)

global SPFunc

%spFunction spNConnect() : connect to the device.
sChannel = SPFunc.NConnect(1,handles.edIPAddress.String);%Interface Type -> 0 : USB, 1 : Ethernet
                                                         %Return Value = Channel
if(sChannel > -1 || sChannel == -116)       
    for n= 1:3
        %spFunction spNConnect() : check device connection.
        bRtn = SPFunc.NCheckConnection(0);    
         if(bRtn == 1)
            break;
        end
        java.lang.Thread.sleep(100);
    end
    if(bRtn==1)                            
        iIndex = sChannel + 1;
        strList = string(handles.popChannelList.String);        
        DevType = libpointer('int16Ptr',0);        
        
        %spFunction spNGetCCDType() : get ccd type of connected device   
        SPFunc.NGetCCDType(DevType,sChannel);                
        if(DevType.value == 0)
            SPFunc.strModel(iIndex) = "SM245N";
        elseif(DevType.value == 1)
            SPFunc.strModel(iIndex) = "SM445N";
        elseif(DevType.value == 2)
            SPFunc.strModel(iIndex) = "SM303N";
        elseif(DevType.value == 3 || DevType.value == 5)
            SPFunc.strModel(iIndex) = "SM304N";
        elseif(DevType.value == 4)
            SPFunc.strModel(iIndex) = "SM642N";        
        end
        
        temp = strcat('ch#',num2str(sChannel));
        strList(iIndex) = strcat(temp,strcat(' : ',SPFunc.strModel(iIndex)));    
        
        SPFunc.bInitial(iIndex) = 0;
        SPFunc.sTotChnl = SPFunc.sTotChnl + 1;
        SPFunc.sDevType(iIndex) = DevType.value;
        handles.popChannelList.String = strList;
        
        handles.btnInitializing.Enable = 'on';
        set(handles.edInitialzing,'String',strcat('Complete Connect Ch : ',num2str(sChannel)));
    else        
        set(handles.edInitialzing,'String',strcat('There is an error on Ehternet Connect.'));
    end
else
    set(handles.edInitialzing,'String',strcat('There is an error on Ehternet Connect.'));   
end

function btnInitializing_Callback(~, ~, handles)

global SPFunc
global WavelengthTable
global Data

%Connected Channel interface check  
for n = 1:SPFunc.sTotChnl        
    if (SPFunc.sDevType(n) == 0)          % SM245N
        SPFunc.sTotPixelNum(n) = 2080;
        SPFunc.sRealPixelNum(n) = 2048;   % The first 32 pixels are optical blank pixels. 
        SPFunc.sStartOpticalBlank(n) = 32;% The real data is 2048 for Sony ILX 511.
        
    elseif (SPFunc.sDevType(n) == 1)      % SM445N
        SPFunc.sTotPixelNum(n) = 3680;
        SPFunc.sRealPixelNum(n) = 3648;   % The first 32 pixels are optical blank pixels.
        SPFunc.sStartOpticalBlank(n) = 32;% The real data is 3648 for TOSHIBA TCD1304AP.
    
    elseif (SPFunc.sDevType(n) == 2)      % SM303N
        SPFunc.sTotPixelNum(n) = 1024;   
        SPFunc.sRealPixelNum(n) = 1024;   %There is no optical blank
        SPFunc.sStartOpticalBlank(n) = 0;
  
    elseif (SPFunc.sDevType(n) == 3)      % SM304N
        SPFunc.sTotPixelNum(n) = 512;     
        SPFunc.sRealPixelNum(n) = 512;    % There is no optical blank
        SPFunc.sStartOpticalBlank(n) = 0; % The real data is 512 for Hamamatsu G9212 InGaAs array detector.
   
    elseif (SPFunc.sDevType(n) == 4)      % SM642N
        SPFunc.sTotPixelNum(n) = 2080;
        SPFunc.sRealPixelNum(n) = 2048;   % The first 10 pixels are optical blank pixels. 
        SPFunc.sStartOpticalBlank(n) = 10;% The real data is 2048 HAMAMATSU S10420.
    end
    
    WavelengthTable = 1:SPFunc.sRealPixelNum(n);
    set(handles.axDataGraph,'XLim',[WavelengthTable(1) WavelengthTable(SPFunc.sRealPixelNum(n))],'xgrid','on','ygrid','on');
    
    Data = zeros(1,SPFunc.sRealPixelNum(n));
    
    SPFunc.sAvgTime(n) = 1;
    SPFunc.lIntTime(n) = 35;
    SPFunc.sTrgMode(n) = 1;       

    % spFunction spNSetDevice() : set the device parameter
    % sTrgMode -> 1: Free Run Previous
    %             2: Free Run Next
    %             3: Software Trigger
    sRtn = SPFunc.NSetDevice(SPFunc.lIntTime(n),SPFunc.sAvgTime(n), SPFunc.sTrgMode(n),n-1); 
    if (sRtn < 0)
        set(handles.edInitialzing,'String',strcat('There is an error on set device the ch#. ',num2str(n-1)))
        SPFunc.bInitial(n) = 0;
        DeactiveCtr(handles)
        return
    end
    
    SPFunc.bInitial(n) = 1;
end

SelCh = handles.popChannelList.Value;

handles.edInitialzing.String = 'All connected channels work OK.';
handles.edIntTime.String = num2str(SPFunc.lIntTime(SelCh));
handles.edAvgTime.String = num2str(SPFunc.sAvgTime(SelCh));
handles.radFreeRunPre.Value = 1;
handles.popChannelList.Enable ='on';
handles.edIntTime.Enable = 'on';
handles.edAvgTime.Enable = 'on';

handles.radFreeRunPre.Enable = 'on';
handles.radFreeRunNext.Enable = 'on';
handles.radSWTrigger.Enable = 'on';

handles.btnFactory.Enable = 'on';
handles.btnPixel.Enable = 'on';
handles.btnUserCal.Enable = 'on';
handles.btnGetData.Enable = 'on';
handles.btnSave.Enable = 'on';

function popChannelList_Callback(~, ~, handles)

global SPFunc

SelCh = handles.popChannelList.Value;

if(SPFunc.bInitial(SelCh) == 0)
    DeactiveCtr(handles);
    handles.btnInitializing.Enable = 'on';    
    handles.popChannelList.Enable ='on';
else
    handles.edIntTime.String = num2str(SPFunc.lIntTime(SelCh));
    handles.edAvgTime.String = num2str(SPFunc.sAvgTime(SelCh));

    handles.radFreeRunPre.Enable = 'on';
    handles.radFreeRunNext.Enable = 'on';
    handles.radSWTrigger.Enable = 'on';
    
    handles.edIntTime.Enable = 'on';
    handles.edAvgTime.Enable = 'on';
    
    handles.btnFactory.Enable = 'on';
    handles.btnPixel.Enable = 'on';
    handles.btnUserCal.Enable = 'on';
    handles.btnGetData.Enable = 'on';
    handles.btnSave.Enable = 'on';
    
    if(SPFunc.sTrgMode(SelCh) == 1)
        handles.radFreeRunPre.Value = 1;
    elseif(SPFunc.sTrgMode(SelCh) == 2)    
        handles.radFreeRunNext.Value = 1;
    else
        handles.radSWTrigger.Value = 1;        
    end
end

function edIntTime_Callback(~, ~, handles)
global SPFunc

SelCh = handles.popChannelList.Value;

SPFunc.lIntTime(SelCh) = str2num(handles.edIntTime.String);

% spFunction spNSetIntTime() : set the integration time
sRtn = SPFunc.NSetIntTime(SPFunc.lIntTime(SelCh),SelCh-1);
if (sRtn < 0)
    set(handles.edInitialzing,'String',strcat('There is an error on set Integration time the ch#. ',num2str(SelCh-1)))
    SPFunc.bInitial(SelCh) = 0;
    DeactiveCtr(handles)
    return
end

function edAvgTime_Callback(~, ~, handles)

global SPFunc

SelCh = handles.popChannelList.Value;

SPFunc.sAvgTime(SelCh) = str2num(handles.edAvgTime.String);

% spFunction spNSetTimeAvg() : set the hardware time average
sRtn = SPFunc.NSetTimeAvg(SPFunc.sAvgTime(SelCh),SelCh-1);
if (sRtn < 0)
    set(handles.edInitialzing,'String',strcat('There is an error on set time average the ch#. ',num2str(SelCh-1)))
    SPFunc.bInitial(SelCh) = 0;
    DeactiveCtr(handles)
    return
end

function radFreeRunPre_Callback(~, ~, handles)

global SPFunc

SelCh = handles.popChannelList.Value;
    
SPFunc.sTrgMode(SelCh) = 1;   % sIntMode -> 1: free run previous, 2 : free run next, 3 : software trigger    

% spFunction spNSetTrgMode() : set the set trigger mode
sRtn = SPFunc.NSetTrgMode(SPFunc.sTrgMode(SelCh),SelCh-1);
if (sRtn < 0)
    set(handles.edInitialzing,'String',strcat('There is an error on trigger mode the ch#. ',num2str(SelCh-1)))
    SPFunc.bInitial(SelCh) = 0;
    DeactiveCtr(handles)
    return
end

function radFreeRunNext_Callback(~, ~, handles)

global SPFunc

SelCh = handles.popChannelList.Value;

SPFunc.sTrgMode(SelCh) = 2;   % sIntMode -> 1: free run previous, 2 : free run next, 3 : software trigger    

% spFunction spNSetTrgMode() : set the set trigger mode
sRtn = SPFunc.NSetTrgMode(SPFunc.sTrgMode(SelCh),SelCh-1);
if (sRtn < 0)
    set(handles.edInitialzing,'String',strcat('There is an error on trigger mode the ch#. ',num2str(SelCh-1)))
    SPFunc.bInitial(SelCh) = 0;
    DeactiveCtr(handles)
    return
end

function radSWTrigger_Callback(~, ~, handles)

global SPFunc

SelCh = handles.popChannelList.Value;

SPFunc.sTrgMode(SelCh) = 3;   % sIntMode -> 1: free run previous, 2 : free run next, 3 : software trigger    

% spFunction spNSetTrgMode() : set the set trigger mode
sRtn = SPFunc.NSetTrgMode(SPFunc.sTrgMode(SelCh),SelCh-1);
if (sRtn < 0)
    set(handles.edInitialzing,'String',strcat('There is an error on trigger mode the ch#. ',num2str(SelCh-1)))
    SPFunc.bInitial(SelCh) = 0;
    DeactiveCtr(handles)
    return
end

function btnPixel_Callback(~, ~, handles)

global SPFunc
global WavelengthTable
global Data
global axXlabel

SelCh = handles.popChannelList.Value;

WavelengthTable = 1:SPFunc.sRealPixelNum(SelCh);

plot(handles.axDataGraph,WavelengthTable,Data,'r');

set(handles.axDataGraph,'XLim',[WavelengthTable(1) WavelengthTable(SPFunc.sRealPixelNum(SelCh))],'xgrid','on','ygrid','on');
axXlabel = 'Pixle';
xlabel(axXlabel);
ylabel('Intensity(a.u)');

function btnFactory_Callback(~, ~, handles)

global SPFunc
global WavelengthTable
global Data
global axXlabel

SelCh = handles.popChannelList.Value;

plWLTable = libpointer('doublePtr',zeros(1,SPFunc.sRealPixelNum(SelCh)));

% spFunction spNGetWLTable() : get WLTable in EEPROM
sRtn = SPFunc.NGetWLTable(plWLTable,SelCh-1);    
if (sRtn < 0)
    set(handles.edInitialzing,'String',strcat('There is an error on get WLTable the ch#. ',num2str(SelCh-1)))
    SPFunc.bInitial(SelCh) = 0;
    DeactiveCtr(handles)
    return
end

temp = get(plWLTable);

WavelengthTable = temp.Value;

plot(handles.axDataGraph,WavelengthTable,Data,'r');

set(handles.axDataGraph,'XLim',[WavelengthTable(1) WavelengthTable(SPFunc.sRealPixelNum(SelCh))],'xgrid','on','ygrid','on');
axXlabel = 'Wavelength(nm)';
xlabel(axXlabel);
ylabel('Intensity(a.u)');

function btnUserCal_Callback(~, ~, handles)

% Spectrometer wavelength vs. pixel number Calibration Data set
% This set was given as an example.
% The customer has to use the proper calibration data set per each channel
global SPFunc
global WavelengthTable
global Data
global axXlabel

SelCh = handles.popChannelList.Value;

if(SPFunc.sDevType(SelCh) == 0)
    filename = 'UserCal\CalData_SM245N.txt';
elseif(SPFunc.sDevType(SelCh) == 1)
    filename = 'UserCal\CalData_SM445N.txt';
elseif(SPFunc.sDevType(SelCh) == 2)
    filename = 'UserCal\CalData_SM303N.txt';
elseif(SPFunc.sDevType(SelCh) == 3)
    filename = 'UserCal\CalData_SM304N.txt';
elseif(SPFunc.sDevType(SelCh) == 4)
    filename = 'UserCal\CalData_SM642N.txt';
end

fileID = fopen(filename);

filetext = fgetl(fileID);
tempstr = strsplit(filetext);
ipoint = str2num(cell2mat(tempstr(2)));

filetext = fgetl(fileID);
filetext = fgetl(fileID);

dWavelength = zeros(1,ipoint);
dPixelNo = zeros(1,ipoint);

for n = 1:ipoint
    filetext = fgetl(fileID);
    tempstr = strsplit(filetext);
    
    dWavelength(n) = str2num(cell2mat(tempstr(1)));
    dPixelNo(n) = str2num(cell2mat(tempstr(2)));
end

pdcoefs = libpointer('doublePtr',zeros(1,5)); 
pdWL = libpointer('doublePtr',dWavelength);
pdPx = libpointer('doublePtr',dPixelNo);

% spFunction spPolyFit() : polynomial fitting
SPFunc.NPolyFit(pdPx,pdWL,ipoint,pdcoefs,3);

pdCalWL = libpointer('doublePtr',zeros(1,1));

for n = 1:SPFunc.sRealPixelNum(SelCh)
    % spFunction spPolyFit() : polynomial calculation
    SPFunc.NPolyCalc(pdcoefs,3,n,pdCalWL);
    WavelengthTable(n) = pdCalWL.Value(1);
end

plot(handles.axDataGraph,WavelengthTable,Data,'r');

set(handles.axDataGraph,'XLim',[WavelengthTable(1) WavelengthTable(SPFunc.sRealPixelNum(SelCh))],'xgrid','on','ygrid','on');
axXlabel = 'Wavelength(nm)';
xlabel(axXlabel);
ylabel('Intensity(a.u)');

function btnGetData_Callback(~, ~, handles)

global SPFunc
global WavelengthTable
global axXlabel
global Data

SelCh = handles.popChannelList.Value;

plData = libpointer('int32Ptr',zeros(1,SPFunc.sTotPixelNum(SelCh)));
lData = zeros(1,SPFunc.sTotPixelNum(SelCh));

% spFunction spNReadDataEx() : read data from the CCD
sRtn = SPFunc.NReadDataEx(plData,SelCh-1);  
if (sRtn < 0)
    set(handles.edInitialzing,'String',strcat('There is an error on get CCD data the ch#. ',num2str(SelCh-1)))
    DeactiveCtr(handles)
    return
end

for m = 1:SPFunc.sTotPixelNum(SelCh)
    lData(m) = lData(m) + plData.Value(m)/SPFunc.sAvgTime(SelCh);
end

if not( SPFunc.sStartOpticalBlank(SelCh) == 0)
    Data = lData(SPFunc.sStartOpticalBlank(SelCh):SPFunc.sRealPixelNum(SelCh) + SPFunc.sStartOpticalBlank(SelCh)-1);
else
    Data = lData(1:SPFunc.sRealPixelNum(SelCh));
end

plot(handles.axDataGraph,WavelengthTable,Data,'r');

set(handles.axDataGraph,'XLim',[WavelengthTable(1) WavelengthTable(SPFunc.sRealPixelNum(SelCh))],'xgrid','on','ygrid','on');
xlabel(axXlabel);
ylabel('Intensity(a.u)');


function btnSave_Callback(~, ~, handles)

global SPFunc
global WavelengthTable
global Data

SelCh = handles.popChannelList.Value;

save_filename = strcat(SPFunc.strModel(SelCh),'-Data.txt');
OUT=fopen(save_filename,'wt');
fprintf(OUT, 'Pixel\tWavelength(nm)\tIntensity\n');

for n = 1:SPFunc.sRealPixelNum(SelCh)
    fprintf(OUT, '%d\t%0.1f\t%d\n',n,WavelengthTable(n),Data(n));
end

fclose(OUT);


function figure1_CloseRequestFcn(hObject, eventdata, handles)

global SPFunc

SPFunc.unLoadLibrary();

delete(hObject);
