function control_log(varargin)
% control_log(action) or control_log(gui callback args, action)
% Callback for handling log events
% May be called directly or as a GUI callback.
% GUI callbacks have the signature:
%   hObject, eventdata, guidata
% where guidata is expected to be the action argument.
%
% action indicates is being performed, such as the adhoc toggle button
% which alternates between logging on and off effort detections.

if length(varargin) > 2
    hObject = varargin{1};
    eventdata = varargin{2};
end
action = varargin{end};

global handles PARAMS TREE HANDLES

switch action
    case 'adhoc'
        % switch between on and off effort 
        
        BgColor = [.80 .80 .80]; % default gray
        String = 'On Effort -> Off Effort';
        % if the button was already pushed revert color back to gray
        if PARAMS.log.adhoc
            PARAMS.log.adhoc = 0;
        else
            %else turn the background red
            PARAMS.log.adhoc = 1;
            String = 'Off Effort -> On Effort';
            BgColor = [.9, .4, .6]; % orchid 
        end
        
        mkChart
        set(handles.adhoc, 'String', String)
        set(handles.logcallgui, 'color',BgColor)
        
    case 'save_effort'
        
        
        % Where should effort be saved?
        [effortfile, effortpath] = uiputfile('.xls', 'Save Effort template to');
        if isnumeric(effortfile)
            return % user cancelled
        end
        
        % Copy the effort template to the location where the user
        % would like to save the effort.
        template = getEffortTemplate();  % Filename for effort template
        saveto = fullfile(effortpath, effortfile);
        copyfile(template, saveto, 'f');

        % Write out the effort
        writeEffort(TREE.rootNode, saveto);
        
    case 'load_effort'
        [logfilename, logfilepath]=uigetfile('*.xls', ...
            'select a spreadsheet');
        
        if isnumeric(logfilename)
            return
        else
            read_effort(fullfile(logfilepath, logfilename))
            set(TREE.tree, 'visible', 0)
            pause(0.0001)
            set(TREE.tree, 'visible', 1)
        end
        
    case 'species'
        value = get(handles.species.pulldown, 'value');
        species = [TREE.speciesR{value}, filesep];
        [TREE.callR,TREE.callW] = species_ordering(species, 2, 1);
        
        % build list of parameters for calls associated with this
        % species
        callAttr = struct('call', TREE.callR);
        % 
        callRange = TREE.position+[1:length(callAttr)];
        pidx = 1;
        for idx = callRange  % for each call
            % Find parameters for this call
            paramsidx =  ~cellfun(@isempty, TREE.dFreq(idx, :));
            callAttr(pidx).params = TREE.dFreq(idx, paramsidx);
            callAttr(pidx).values = NaN * zeros(length(callAttr(pidx).params),1);
            pidx = pidx + 1;
        end

        % Save the attributes associated with the calls for this species
        set(hObject, 'UserData', callAttr);
        % Set up the call checkboxes and any needed parameters
        init_callcheckboxes(callAttr);

        
    case 'group'
        value = get(handles.group.pulldown, 'value');
        group = [TREE.groupR{value}, filesep];
        [TREE.speciesR,TREE.speciesW] = species_ordering(group);
        set(handles.species.pulldown,'string', TREE.speciesR)
        
        set(handles.species.pulldown, 'value', 1)
        control_log(handles.species.pulldown, [], 'species');
        
    case 'log'
        logcallxls
        
    case 'savejpg'
        color = get(handles.savejpegbutton, 'BackgroundColor');
        % save current button label in case we fail
        button_label = get(handles.savejpegbutton, 'String');
        set(handles.savejpegbutton, 'BackgroundColor', color*.9);
        if isempty(handles.log.image)
            handles.log.image = fullfile(handles.log.imagedir, 'temp.jpg');
            try
                saveas(HANDLES.fig.main, handles.log.image);
                button_label = 'Clear Image';
            catch
                handles.log.image = [];
                errordlg('Unable to save plot window');
            end
        else
            button_label = 'Save Image';
            handles.log.image = [];
        end
        set(handles.savejpegbutton, 'BackgroundColor', color, ...
            'String', button_label);

        
    case 'mkXWAV'
        color = get(handles.savexwavbutton, 'BackgroundColor');
        % save current button label in case we fail
        button_label = get(handles.savexwavbutton, 'String');
        set(handles.savexwavbutton, 'BackgroundColor', color*.9);
        if isempty(handles.log.audio)
            try
                fname = fullfile(handles.log.audiodir, 'temp.x.wav');
                savexwav(fname);
                handles.log.audio = fname;
                button_label = 'Clear Audio';
            catch e
                button_label = 'Save Audio';
                badfield(handles.savexwavbutton, e.message, .75);
            end
        else
            button_label = 'Save Audio';
            handles.log.audio = [];
        end

        set(handles.savexwavbutton, 'BackgroundColor', color, ...
            'String', button_label);
                
    case 'set_metadata'
        
        % Verify user has filled in requested fields before proceeding
        fields = {'project', 'deploy', 'site', 'user', 'effort_start'};
        WorksheetNames = {'Project', 'Deployment', 'Site', 'User ID', ...
            'Effort Start'};
        values = cell(length(fields),1);
        bad = zeros(1, length(fields));
        for fidx = 1:length(fields)
            values{fidx} = get(handles.(fields{fidx}).disp, 'String');
            bad(fidx) = isempty(values{fidx});
            % Additional checking
            switch fields{fidx}
                case 'effort_start'
                    % Verify date format
                    try
                        values{fidx} = datenum(values{fidx});
                        values{fidx} = datestr(values{fidx}, 31);
                    catch excep
                        bad(fidx) = true;
                    end
                case 'deploy'
                    % Verify deployment is numeric
                    [v, ok] = str2num(values{fidx});
                    if ~ ok
                        bad(fidx) = true;
                    end
            end
        end
        
        if sum(bad) > 0
            red = [1 0 0];
            % one or more fields not filled in, flash the bad fields
            oldcolors = size(length(bad), length(red));
            for b = find(bad)
                oldcolor(b,:) = get(handles.(fields{b}).disp, 'BackgroundColor');
                set(handles.(fields{b}).disp, 'BackgroundColor', red);
            end
            pause(.5);
            for b = find(bad)
                set(handles.(fields{b}).disp, 'BackgroundColor', oldcolor(b,:));
            end
            return  % bail out
        end
        
        % Save metadata
        1;
        % Turn off effort start callback
        PARAMS.log.pick = [];  
        pickxyz(true);  % set cursor appropriately
        
        % Passed initial integrity checks, request effort
        set(handles.effortL, 'Visible', 'on');
        set(handles.effortS, 'Visible', 'on');
        set(handles.log.effort, 'Visible', 'off');  % Hide metadata
        set(handles.done, 'Visible', 'off');
        log_open(WorksheetNames, values);
        set(handles.effort, 'Visible', 'on');
        set(TREE.tree, 'Visible', 1); % overlay effort tree

        
        % New log, make sure that on/off effort detections are empty
        for f = {'OnEffort', 'OffEffort'}
            f = f{1};
            RowsN = handles.(f).Sheet.UsedRange.Rows.Count;
            % Clear all used rows after headers
            if RowsN > 1
                Range = handles.(f).Sheet.Range(sprintf('2:%d', RowsN));
                Range.Clear();  % clear out any data
                Range.EntireRow.Delete();  % remove rows
            end
        end
        
    case {'set_effort', 'append'}
        if strcmp(action, 'set_effort')
            set(handles.effort, 'Visible', 'off');
            set(TREE.tree, 'Visible', 0);  % Hide the effort tree
            writeEffort(TREE.rootNode, handles.Workbook);
            % No need to open the log, we already did so.
        else
            read_effort(handles.logfile);
            log_open();
        end
            
        % Hide the effort, effort load and save buttons
        set(handles.effortL, 'Visible', 'off');
        set(handles.effortS, 'Visible', 'off');
        set(handles.effort, 'Visible', 'off');

        % enable logging controls
        set(handles.log.control, 'Visible', 'on');
        mkChart;  % Populate initial Group & Species pulldowns
        PARAMS.log.pick = 'timeXfreq';
        pickxyz(true);  % set up selection cursor

    case 'set_meta_end'
        endeffort = get(handles.effort_end.disp, 'String');
        enddate = datenum(endeffort);  % convert to serial date
        if ~isempty(handles.log.lastDate) && enddate < handles.log.lastDate
            badfield(handles.effort_end.disp, 'Before last detection', .5);
            return
        end
        if ~ isempty(handles.log.endDate) && handles.log.endDate > enddate
            choice = questdlg(sprintf('Selected end of effort %s < existing end %s', ...
                datestr(enddate, 'yyyy/mm/dd HH:MM:SS'), ...
                datestr(handles.log.endDate, 'yyyy/mm/dd HH:MM:SS')), ...
                'Effort will be reduced', ...
                'Proceed', 'Choose new date', 'Choose new date');
            if strcmp(choice, 'Choose new date');
                return
            end
        end
        log_close(enddate);

        
    case 'set_parameter'
        % User has pressed one of the set parameter buttons.  Copy
        % value from the current pick.

        % Retrieve the last selected time X frequency
        tf = get(handles.timefreq, 'UserData');
        if isempty(tf)
            % No selections yet
            return
        end
        
        % Retrieve the call attributes
        callAttr = get(handles.species.pulldown, 'UserData');
        
        % Determine which call and parameter is to be set.
        map = get(hObject, 'UserData');
        callidx = map(1);
        paramidx = map(2);
        buttonidx = map(3);
        
        % if user pressed a parameter button, copy value in from
        % last pick
        change = false;
        if find(handles.freq == hObject) 
            if ~ isempty(tf{1}.freq)
                % Currently only storing frequencies via button mechanism
                callAttr(callidx).values(paramidx) = tf{1}.freq;
                set(handles.species.pulldown, 'UserData', callAttr);
                set(handles.freqdisplay(buttonidx), ...
                    'String', num2str(tf{1}.freq));
                change = true;
            end
        else
            % User entered value via text box.  Save it
            value = str2num(get(hObject, 'String'));
            if isempty(value)
                % Bad value, restore previous one
                set(gcbo, 'String', ...
                    num2str(callAttr(callidx).values(paramidx)));
            else
                callAttr(callidx).values(paramidx) = value;
                change = true;
            end
        end
        % Store call attributes if they have changed
        if change
            set(handles.species.pulldown, 'UserData', callAttr);
        end
        
    case {'show_lastentry'}
        % Show an abbreviated form of the last log entry
        
        % Data for detection entry has been gathered, determine where it
        % will be stored.
        if PARAMS.log.adhoc
            % off effort
            detection = handles.OffEffort;
        else
            detection = handles.OnEffort;
        end

        % Add one row for each call that is being logged
        currentRow = detection.Sheet.UsedRange.Rows.Count;
        % find start/end time
        % todo:  call new function to format...
        
    case {'pickstart', 'pickend'}
        field = sprintf('%sdisplay', action);
        posn = strcmp(action, {'pickstart', 'pickend'});
        time_from_pick(handles.timefreq(posn), handles.(field));
        
    case 'pickboth'
        tf(1) = time_from_pick(handles.timefreq(1), handles.pickstartdisplay);
        tf(2) = time_from_pick(handles.timefreq(2), handles.pickenddisplay);

        minmax = true;
        if minmax && ~isempty(tf(1).freq)
            minFreq = min(tf.freq);
            maxFreq = max(tf.freq);
            callAttr = get(handles.species.pulldown, 'UserData');
            selected = get(handles.calltype, 'Value');
            if iscell(selected)
                selected = cell2mat(selected);
            end
            changed = false;
            for cidx=1:length(selected)
                if selected(cidx)
                    % Look for min or max
                    % Must be either just the word min|max
                    % or start with min.* or max.* and have the 
                    % characters "freq" somewhere within the string
                    minidx = find(~cellfun(@isempty, ...
                        regexp(callAttr(cidx).params, ...
                        '^min(.*freq.*)?$', 'ignorecase')), 1, 'first');
                    if ~ isempty(minidx)
                        changed = true;
                        callAttr(cidx).values(minidx)= minFreq;
                        % Update the parameters text box if it is 
                        % currently displayed
                        displayCallParam(cidx, minidx, minFreq);
                    end
                    
                    maxidx = find(~cellfun(@isempty, ...
                        regexp(callAttr(cidx).params, ...
                        '^max(.*freq.*)?$', 'ignorecase')), 1, 'first');
                    if ~ isempty(maxidx)
                        changed = true;
                        callAttr(cidx).values(maxidx) = maxFreq;
                        % Update the parameters text box if it is 
                        % currently displayed
                        displayCallParam(cidx, maxidx, maxFreq);
                    end
                end
            end
            if changed
                set(handles.species.pulldown, 'UserData', callAttr);
            end
        end
end

    function displayed = displayCallParam(cidx, pidx, value)
        % displayed = displayCallParam(cidx, pidx, value)
        % Look through the list of displayed call parameters to
        % see if the cidx'th call has the pidx'th parameter displayed.
        % If it does, set the value.
        
        displayed = false;  % Assume not displayed until we find it
        idx = 1;
        while ~displayed && idx <= length(handles.freq)
            % Each parameter box has the call number and parameter
            % stored in the user data
            map = get(handles.freq(idx), 'UserData');
            if map(1) == cidx && map(2) == pidx
                % We found the right one, update it and bail out of loop
                displayed = true;
                set(handles.freqdisplay(idx), 'String', num2str(value));
            else
                idx = idx + 1;  % check the next one
            end
        end
    end
       
        
    function tf = time_from_pick(pickH, timeH)
        % tf = time_from_pick(pickH, timeH)
        % Given a handle to a pick field, copy the information
        % associated with the pick to a time display and format
        % the time string.
        tf = get(pickH, 'UserData');
        if isstruct(tf) && isfield(tf, 'time')
            timestr = datestr(tf(1).time, 'YYYY-mm-DD HH:MM:SS.FFF');
            set(timeH, 'String', timestr, 'UserData', tf);
        end
    end
        

    function init_callcheckboxes(callAttribs)
        handles.prev = 0;
        calls = {callAttribs.call};
        %handles.freqDisp = cell(length(calls), PARAMS.numfreq);
        
        % Determine layout for checkboxes
        c = 2;
        if length(calls) > 4
            r = ceil(length(calls)/c);
        else
            r = 3;
        end
        h = 1/r;
        w = 1/c;
        sep = w/10;
        bgColor = [0.8 0.3 0.8];
        for ci = 1:c
            x(:,ci) = ((ci-1)/c) .* ones(r,1);
            y(:,ci) = h .* [r-1:-1:0]';
        end
        
        k = 1;
        callLength = length(calls);
        for cx = 1 : c
            for cy = 1:r
            callbtnpos{k} = [x(1,cx)+sep, y(cy,1), w-sep, h];
            k = k+1;
            end
        end
        
        % Remove any existing buttons
        delete(get(handles.speciesbuttons,'children'));
        handles.calltype = [];
        
        for i = 1:length(callAttribs)
            handles.calltype(i) = uicontrol('style','checkbox',...
                'String',calls{i},...
                'Units', 'normalized',...
                'pos',callbtnpos{i},...
                'backgroundcolor', bgColor,...
                'Parent',handles.speciesbuttons,...
                'Callback', {@call_checkbox, i});
        end
        if length(callAttribs) == 1
            set(handles.calltype(1), 'Value', 1);
        end
        set(handles.speciesbuttons, 'selectedObject', [])
        
        % Initialize parameters by invoking checkbox callback
        % with fake values
        call_checkbox([], [], []);
    end

    function call_checkbox(objectH, eventdata, call_idx)
    % call_checkbox = (objectH, eventdata, call_idx)
    % Callback for select/deselect a call type
    
    % Populate parameters for selected calls
    
    paramsN = length(handles.freq);  % Maximum # parameters
    
    % Retrieve information about calls and possible parameters
    callAttr = get(handles.species.pulldown, 'UserData');
    done = false;
    % Populate parameters up to paramsN then stop
    cidx = 1;  % call index
    used = 0;  % # parameters used
    while cidx <= length(callAttr) && used < paramsN;
        if get(handles.calltype(cidx), 'Value')
            % call has been selected, populate attributes up to
            % the last available slot
            for aidx = 1:min(length(callAttr(cidx).params), ...
                    paramsN - used)
                used = used + 1;
                % Attribute map
                % Note current call, current attribute, and position 
                % in the list of parameters.  This will let us find
                % the call, attribute, and button/edit box index from
                % a callback.
                attributeMap = [cidx, aidx, used];
                % Set label and value & attribute map
                set(handles.freq(used), 'String', ...
                    sprintf('%d %s %s', used, ...
                        callAttr(cidx).params{aidx}, callAttr(cidx).call), ...
                    'Visible', 'on', 'UserData', attributeMap);
                % Store the attributeMap and set the current value of the
                % call attribute
                set(handles.freqdisplay(used), 'String', ...
                    num2str(callAttr(cidx).values(aidx)), ...
                    'Visible', 'on', 'UserData', attributeMap);
            end
        end
        cidx = cidx+1;
    end
    
    % Make remaining parameters invisible
    for rest = used+1:paramsN
        set(handles.freq(rest), 'Visible', 'off');
        set(handles.freqdisplay(rest), 'Visible', 'off');
    end
    
    end  % end function

    function string = find_subtype(calltype)
        string = [];
        for i = 1:length(calltype)
             call = [calltype{i}, filesep];
             [subtypeR,subtypeW] = species_ordering(call);
             if isempty(subtypeR) || strcmp(subtypeR(1),calltype(1))                 
                 subtypeR = [];
              end
             subtypeR = find_subtype(subtypeR);
             if isempty(subtypeR)
                 string{length(string)+1} = calltype{i};
             else
                 for x = 1:length(subtypeR)
                     string{length(string)+1} = [call,subtypeR{x}];
                 end
             end
        end
    end

    function mkChart
        [TREE.textR,TREE.textW, TREE.orderR, TREE.dFreq] = disp_sect;
        if ~isempty(TREE.textR)
            [TREE.groupR,TREE.groupW] = species_ordering('root');
            %set the group string to the group
            
            set(handles.group.pulldown, 'value', 1)
            set(handles.group.pulldown,'string', TREE.groupR)
            
            [TREE.speciesR,TREE.speciesW] = species_ordering([TREE.groupR{1}, filesep],1,1);
            
            set(handles.species.pulldown, 'value', 1)
            set(handles.species.pulldown,'string', TREE.speciesR)
            
            
            [TREE.callR,TREE.callW] = species_ordering([TREE.speciesR{1}, filesep],2,1);
            control_log(handles.species.pulldown, [], 'species');

        end
    end
end
