
function visualizer()

%% BUILD INTERFACE

% Global variables
video_dir = './';
vinfo = []; total_frames = 0; FPS = 30; 
trk = [];
feat = [];
actions = init_actions;

curr_frame = 0;
prev_frame = 1;
curr_feat = 1;
active_fly = 1;
curr_beh = 1;
curr_beh_type = 1;
curr_flies = [];
active_bout = [];

do_play = false;
stop_bout_play = 0;
updating = 0;
plt.trk = 1; plt.ellipse = 1; plt.wings = 1; plt.tail = 100;

axis_lim = [];
clrs = [];
files = [];

video_h = [];
track_h = [];
behavior_h = [];

% --- MAIN WINDOW
% Detect screen size
scrsz = get(0,'ScreenSize');
height = 780;%if screen hight is shorter then decrese it here
width = height*1.25;
fig_h = figure('Position',[(scrsz(3)-width)/2 (scrsz(4)-height)/2 width height], 'Menubar', 'none','resize','off','Name','Visualizer','NumberTitle','off');
set(fig_h,'WindowButtonDownFcn',@mouse_pressed)
set(fig_h,'CloseRequestFcn',@myclose)
% Menuitems (Video, Track, GroundTruth, Prediction
vid_h = uimenu(fig_h,'Label','Video');
uimenu(vid_h,'Label','Open','Callback',@open_video);
trk_h = uimenu(fig_h,'Label','Track');
uimenu(trk_h,'Label','Open without video','Callback',@open_trk_novid)
trk_vid_h = uimenu(trk_h,'Label','Open for current video','Callback',@open_trk,'enable','off');
gtm_h = uimenu(fig_h,'Label','GroundTruth','enable','off');
uimenu(gtm_h,'Label','Open','Callback',@open_gtlabel);
predm_h = uimenu(fig_h,'Label','Prediction','enable','off');
uimenu(predm_h,'Label','Open','Callback',@open_predlabel);

% --- LEFT PANEL
% Image window
figsz = get(fig_h,'Position');
figclr = get(fig_h,'color');
subpl_clr = figclr-0.02;
ctrl_clr = figclr+0.04;
m_pix = .05*min(figsz(3:4)); 
m_v = m_pix/figsz(4);
m_h = m_pix*1.5/figsz(3);
p_v = .6; % percent
p_h = height*p_v*640/480/width;
vidpos = [m_h 1-p_v-m_v p_h p_v];
vidplotpos = vidpos;
sub_h = subplot('Position',vidplotpos);
set(sub_h,'XTick',[]);
set(sub_h,'YTick',[]); 
set(sub_h,'color',subpl_clr); box on

hsz = figsz(3);
vsz = figsz(4);
buff = 5;
pos_v = (1-p_v-m_v)*vsz - 15 - buff;
pos_h = m_pix*1.5;

% Video label
position = [pos_h/hsz 1-m_v+3/vsz p_h 15/vsz];
label_h = load_uicontrol('text',position,'',figclr);

% Navigation slider  
position = [pos_h/hsz pos_v/vsz p_h+1/hsz 15/vsz];
video_h(1) = load_uicontrol('slider',position,[],'default',@vid_slider,[],0);
set(video_h(1),'min',0,'max',1)
% Navigation controls
pos_v = pos_v - 27;
position = [pos_h/hsz pos_v/vsz 50/hsz 25/vsz];
video_h(end+1) = load_uicontrol('pushbutton',position,'play',[.3 .7 .4],@play_vid,'left');
pos_h = pos_h+65;
          
position = [pos_h/hsz (pos_v)/vsz 60/hsz 25/vsz];
video_h(end+1) = load_uicontrol('edit',position,'0',figclr,@go_to_frame);

position = [(pos_h+60)/hsz pos_v/vsz 20/hsz 20/vsz];
video_h(end+1) = load_uicontrol('text',position,'/',figclr);

position = [(pos_h+80)/hsz pos_v/vsz 70/hsz 20/vsz];
video_h(end+1) = load_uicontrol('text',position,'0',figclr,[],'left');

position = [(pos_h+300)/hsz pos_v/vsz 100/hsz 25/vsz];
video_h(end+1) = load_uicontrol('pushbutton',position,'undo jump',[.7 .3 .3],@go_back_to_frame);
set(video_h(end),'visible','off')
for hh=1:numel(video_h)
    set(video_h(hh),'enable','off')
end

% Pan and zoom buttons
zoom_imgs = get_icons();
pos_h = m_pix*1.5 + p_h*hsz - 72;
pos_v = pos_v + 5;
% - zoom in
position = [pos_h/hsz pos_v/vsz 20/hsz 20/vsz];
zoom_h(1) = load_uicontrol('pushbutton',position,'','default',@zoomin);  
% - zoom out
position = [(pos_h+26)/hsz pos_v/vsz 20/hsz 20/vsz];
zoom_h(2) = load_uicontrol('pushbutton',position,'','default',@zoomout);      
% - pan
position = [(pos_h+26*2)/hsz pos_v/vsz 20/hsz 20/vsz];
zoom_h(3) = load_uicontrol('pushbutton',position,'','default',@panfunc);
for hh=1:3
    set(zoom_h(hh),'cdata',zoom_imgs{hh})
end

% GT and pred visualization
rem = pos_v/vsz -m_v*2.5;
temp_v = pos_v/vsz-rem/6-m_v/2.5;
position = [1/hsz temp_v-5/vsz m_pix*1.3/hsz rem/6];
gtl_h = load_uicontrol('text',position','GT:',figclr,[],'right');
set(gtl_h,'enable','off')

gtplotpos = [m_h temp_v-5/vsz p_h rem/6];
gt_h = subplot('Position',gtplotpos);
set(gt_h,'XTick',[]);
set(gt_h,'YTick',[]); 
set(gt_h,'color',subpl_clr); box on

temp_v = pos_v/vsz-2*rem/6-m_v;
position = [1/hsz temp_v+5/vsz m_pix*1.3/hsz rem/6];
predl_h = load_uicontrol('text',position,'Pred.:',figclr,[],'right');
set(predl_h,'enable','off');

predplotpos = [m_h temp_v+5/vsz p_h rem/6];      
pred_h = subplot('Position',predplotpos);
set(pred_h,'XTick',[]);
set(pred_h,'YTick',[]); 
set(pred_h,'color',subpl_clr); box on

% Feature
temp_v = pos_v/vsz-rem-m_v*1.5;
position = [1/hsz temp_v m_pix*1.3/hsz 4*rem/6];
featl_h(1) = load_uicontrol('text',position','Feat.:',figclr,[],'right');
set(featl_h(1),'enable','off','visible','off')

featplotpos = [m_h temp_v-3/vsz p_h 4*rem/6];      
feat_h = subplot('Position',featplotpos);
set(feat_h,'XTick',[]);
set(feat_h,'YTick',[]);
set(feat_h,'color',subpl_clr); box on

position = [m_h+p_h-163/hsz temp_v+.02+4*rem/6-29/vsz 170/hsz 30/vsz];
featl_h(end+1) = load_uicontrol('popup',position,'feature',figclr,@set_feat,[],1);
set(featl_h(end),'enable','off')

% --- RIGHT PANEL
pos_h = m_pix*1.5+(p_h+m_h/1.2)*hsz;
fg_color = .3*[1 1 1];%[0.1 0.5 0.3];
rem = 1-m_h*2-p_h;

position = [pos_h/hsz 0 rem*1.2 height/vsz];
load_uicontrol('text',position,'',ctrl_clr);
position = [pos_h/hsz 0 1.5/hsz height/vsz];
load_uicontrol('text',position,'',.7*[1 1 1]);

% Tracking controls
pos_h = pos_h + m_h/2*hsz;
margin = 1-m_v*3;%.5+m_v+170/vsz;%  1-m_v-110/vsz;
position = [pos_h/hsz margin rem*.7 30/vsz];
string = 'VISUAL CONTROL';
hh = load_uicontrol('text',position,string,[],[],'left');
set(hh,'foregroundcolor',fg_color,'fontsize',14)

% - display options      
margin = margin-25/vsz;
buff = 23/vsz;
position = [pos_h/hsz margin rem/2 30/vsz];
track_h(end+1) = load_uicontrol('checkbox',position,'show tracks',[],@show_all,[],1);

position = [pos_h/hsz+20/hsz margin-1.1*buff rem/2 30/vsz];
track_h(end+1) = load_uicontrol('checkbox',position,'show trail',[],@show_track,[],1);

position = [pos_h/hsz+20/hsz margin-2.1*buff rem/2 30/vsz];
track_h(end+1) = load_uicontrol('checkbox',position,'show ellipses',[],@show_ellipse,[],1);
                    
position = [pos_h/hsz+20/hsz margin-3.1*buff rem/2 30/vsz];
track_h(end+1) = load_uicontrol('checkbox',position,'show wings',[],@show_wings,[],1);
      
% disable until files are loaded
for hh=1:numel(track_h)
    set(track_h(hh),'enable','off')
end
      
% Behavior controls
margin = .5-m_v;
position = [pos_h/hsz margin rem*.7 30/vsz];
string = 'BOUT NAVIGATION';
hh = load_uicontrol('text',position,string,[],[],'left');
set(hh,'foregroundcolor',fg_color,'fontsize',14)

margin = margin-35/vsz;
position = [pos_h/hsz margin 180/hsz 30/vsz];
behavior_h(end+1) = load_uicontrol('text',position,'Select action class:',[],[],'left');
      
position = [pos_h/hsz margin-18/vsz 180/hsz 30/vsz];
behavior_h(end+1) = load_uicontrol('popup',position,'action',[],@set_beh,[],1);
      
margin = margin-55/vsz;
position = [pos_h/hsz margin 180/hsz 30/vsz];
behavior_h(end+1) = load_uicontrol('text',position,'Select bout type:',[],[],'left');
      
position = [pos_h/hsz margin-18/vsz 180/hsz 30/vsz];
behavior_h(end+1) = load_uicontrol('popup',position','ground truth',[],@set_beh_type,[],1);

% - go to bout
margin = margin-67/vsz;  
pos_h = pos_h + 3;
position = [pos_h/hsz margin 15/hsz 20/vsz];
behavior_h(end+1) = load_uicontrol('pushbutton',position,'<','default',@bout_down);

position = [(pos_h+20)/hsz margin-2/vsz 35/hsz 25/vsz];
behavior_h(end+1) = load_uicontrol('edit',position,'0',[],@set_bout);
      
position = [(pos_h+60)/hsz margin 15/hsz 20/vsz];
behavior_h(end+1) = load_uicontrol('pushbutton',position,'>','default',@bout_up);      

position = [(pos_h+75)/hsz margin-2/vsz 15/hsz 20/vsz];
behavior_h(end+1) = load_uicontrol('text',position,'/');

position = [(pos_h+90)/hsz margin-2/vsz 35/hsz 20/vsz];
behavior_h(end+1) = load_uicontrol('text',position,'0',[],[],'left');

position = [(pos_h+110)/hsz margin-2/vsz 60/hsz 25/vsz];
behavior_h(end+1) = ...
    load_uicontrol('pushbutton',position,'play clip',[.3 .7 .4],{@play_bout,'beh'});
      
margin = margin + 24/vsz;
pos_h = pos_h - 3;
position = [pos_h/hsz margin 180/hsz 18/vsz];
behavior_h(end+1) = load_uicontrol('text',position,'Navigate bouts:',[],[],'left');

% disable behavior components until label files are loaded            
for hh=1:numel(behavior_h)
    set(behavior_h(hh),'enable','off')
end
     
%% HELP functions
function handle = load_uicontrol(style,position,string,bgclr,callback,align,value)
    %position([1 3]) = position([1 3])/hsz;
    %position([2 4]) = position([2 4])/vsz;
    if nargin < 3 || isempty(string),   string = '';        end
    if nargin < 4 || isempty(bgclr),    bgclr = ctrl_clr;   end
    if nargin < 5 || isempty(callback), callback = [];      end
    if nargin < 6 || isempty(align),    align = 'center';   end
    if nargin < 7 || isempty(value),    value = 0;          end
    handle = uicontrol(fig_h,'style',style,...
      'units','normalized',...
      'position',position,...
      'string',string,...
      'horizontalAlignment',align,...
      'backgroundcolor',bgclr,...
      'fontsize',12,...
      'value',value,...
      'callback',callback);   
end
    
function init_video
    files = [];
    sub_h = subplot('Position',vidplotpos); cla; axis auto; 
    set(sub_h,'XTick',[]);
    set(sub_h,'YTick',[]); 
    set(sub_h,'color',subpl_clr); box on
    set(label_h,'string','')
    init_track    
end

function actions = init_actions
    actions.beh_labels = {};
    actions.beh_colors = [];
    actions.gt = [];
    actions.pred = [];
    actions.fn = [];
    actions.fp = [];
    actions.curr_bouts = [];
    actions.curr_frames = [];
    actions.overlapTHRESH = 0.1;   
end

function init_track
    gt_h = subplot('Position',gtplotpos); cla; axis auto; 
    set(gt_h,'XTick',[]);
    set(gt_h,'YTick',[]); 
    set(gt_h,'color',subpl_clr); box on
    pred_h = subplot('Position',predplotpos); cla; axis auto; 
    set(pred_h,'XTick',[]);
    set(pred_h,'YTick',[]); 
    set(pred_h,'color',subpl_clr); box on
    feat_h = subplot('Position',featplotpos); cla; axis auto; 
    set(feat_h,'XTick',[]);
    set(feat_h,'YTick',[]); 
    set(feat_h,'color',subpl_clr); box on
    files = [];
    trk = [];
    feat = [];    
    actions = init_actions; 
    curr_frame = 0;
    curr_feat = 1;
    curr_beh = 1;
    curr_beh_type = 1;
    active_fly = 1;
    active_bout = [];
    do_play = false;
    stop_bout_play = 0;
    updating = 0;
    axis_lim = [];
    clrs = [];
    for j=1:numel(track_h)
        set(track_h(j),'enable','off')
    end    
    set(behavior_h(2),'value',1);
    set(behavior_h(4),'value',1);
    for j=1:numel(behavior_h)
        set(behavior_h(j),'enable','off')
    end    
end

function icon_imgs = get_icons()
    icon_names = {'tool_zoom_in.png','tool_zoom_out.png','tool_hand.png'};
    icon_imgs = cell(1,numel(icon_names)*2);
    for j=1:numel(icon_names)
        cdata = imread(fullfile(matlabroot, ...
            ['/toolbox/matlab/icons/' icon_names{j}]));
        cdata = double(cdata)./(2^16-1);
        r = cdata(:,:,1); g = cdata(:,:,2); b = cdata(:,:,3);
        inds = find((r+g+b)==0);
        r(inds) = figclr(1);
        g(inds) = figclr(2);
        b(inds) = figclr(3);
        icon_im = cdata; icon_im_active = cdata;
        icon_im(:,:,1) = r; icon_im(:,:,2) = g; icon_im(:,:,3) = b;
        r(inds) = figclr(1)-.2;
        g(inds) = figclr(2)-.2;
        b(inds) = figclr(3)-.2;
        icon_im_active(:,:,1) = r; icon_im_active(:,:,2) = g; icon_im_active(:,:,3) = b;    
        icon_imgs{j} = icon_im;
        icon_imgs{j+numel(icon_names)} = icon_im_active;
    end   
end

function filename = absolute_path(filename)
   % break filename into parts
   [pathstr,name,ext] = fileparts(filename);
   % turn path into an absolute path
   if (~ismember(':',pathstr))
      indsep = find(pathstr == filesep, 1);
      if (isempty(indsep) || (indsep > 1))
         wd = cd;
         pathstr = fullfile(wd, pathstr);
      end
   end
   % reassemble path
   filename = fullfile(pathstr,[name ext]);
end

function vinfo = video_open(filename)
  filename = absolute_path(filename);
  % store name and type
  vinfo.filename = filename;
  vinfo.type = 'vidobj';
  % read file
  vinfo.vidobj = VideoReader(filename);
  % store number of frames
  vinfo.n_frames = vinfo.vidobj.NumberOfFrames;
  % store frame size
  vinfo.sx = vinfo.vidobj.Height;
  vinfo.sy = vinfo.vidobj.Width;  
end

function im = video_read_frame(vinfo, id)
  im = read(vinfo.vidobj,id+1);  
end

function varargout = draworiellipse(x,varargin)
    % draw an isosceles triangle with center (x,y)
    % with rotation theta
    % with height maj*4
    % with base min*4
    if isstruct(x),
      fly = x;
      if nargin == 1 || ~isnaturalnumber(varargin{1}),
        x = [fly.x];
        y = [fly.y];
        theta = [fly.theta];
        a = [fly.a];
        b = [fly.b];
      else
        t = varargin{1};
        varargin = varargin(2:end);
        x = fly.x(t);
        y = fly.y(t);
        theta = fly.theta(t);
        a = fly.a(t);
        b = fly.b(t);
      end
    else
      if nargin < 5,
        error('not enough arguments; usage: drawflyo(x,y,theta,a,b,...)');
      end
      y = varargin{1};
      theta = varargin{2};
      a = varargin{3};
      b = varargin{4};
      varargin = varargin(5:end);
    end

    phi = -0.03:0.01:2*pi;

    nx = numel(x); ny = numel(y);
    na = numel(a); nb = numel(b); 
    ntheta = numel(theta);
    n = max([nx,ny,na,nb,ntheta]);
    h = zeros(1,n);

    for i = 1:n,
      X1 = a(min(i,na))*cos(phi);
      Y1 = b(min(i,nb))*sin(phi);
      costheta = cos(theta(min(i,ntheta)));
      sintheta = sin(theta(min(i,ntheta)));
      X = costheta*X1 - sintheta*Y1 + x(min(i,nx));
      Y = sintheta*X1 + costheta*Y1 + y(min(i,ny));
      h(i) = plot(X,Y,varargin{:});
    end

    ori_vec = [cos(theta) sin(theta)]*a;
    plot([x x+ori_vec(1)],[y y+ori_vec(2)],varargin{:})
    ori_vec = [cos(theta+pi/2) sin(theta+pi/2)]*b;
    plot([x x+ori_vec(1)],[y y+ori_vec(2)],varargin{:})

    if nargout > 0,
      varargout{1} = h;
    end
end


%% Update interface functions
function update_display(frame)
    if updating 
        return
    end
    updating = 1;
    % update image
    fr = frame+1;
    if isempty(vinfo)
        img = .7*ones(axis_lim(4),axis_lim(2));
    else
        vinfo_ = vinfo;
        try
            img = video_read_frame(vinfo_,frame);
        catch
            disp(['Warning: could not read frame ' num2str(fr)])
            img = video_read_frame(vinfo_,frame-1);
        end 
    end
    figure(fig_h)
    ax = axis(sub_h);
    sub_h = subplot('Position',vidplotpos);
    hold off
    %imagesc(img); colormap gray
    imshow(img); colormap gray
    axis equal; 
    set(sub_h,'XTick',[]);
    set(sub_h,'YTick',[]); 
    if ax(1) > 0 
        axis(ax), 
    elseif ~isempty(axis_lim) 
        axis(axis_lim); 
    end
    set(video_h(3),'string',num2str(fr))
    set(video_h(1),'value',frame)
    
    % update track
    if ~isempty(trk)
        hold on
        %for o=1:size(trk.data,1)
        for oo=1:numel(curr_flies)
            o = curr_flies(oo);
            pos   = trk.data(o,fr,1:2);
            ori   = trk.data(o,fr,3);
            axes  = trk.data(o,fr,4:5);
            lwing = trk.data(o,fr,6:7);
            rwing = trk.data(o,fr,8:9);     
            if plt.trk
                first_fr = max(1,frame-plt.tail);
                x_s = trk.data(o,first_fr:(fr),1);
                y_s = trk.data(o,first_fr:(fr),2);
                plot(x_s,y_s,'-','color',clrs(o,:))
                plot(pos(1),pos(2),'.','color',clrs(o,:),'markersize',20)
            end
            if plt.ellipse
                draworiellipse(pos(1),pos(2),-ori,axes(1)/2,axes(2)/2,'color',clrs(o,:),'linewidth',2)
            end
            if plt.wings
                plot([pos(1) lwing(1)], [pos(2) lwing(2)],'-g','linewidth',1)
                plot(lwing(1),lwing(2),'.g','markersize',10)
                plot([pos(1) rwing(1)], [pos(2) rwing(2)],'-y','linewidth',1)
                plot(rwing(1),rwing(2),'.y','markersize',10)
            end      
        end
        active_pos = trk.data(active_fly,fr,1:2);
        plot(active_pos(1),active_pos(2),'*w','markersize',10);
    end
    
    % update gt
    n_show = 60; % show 60 frames on either side of current frame
    framerange = max(fr-n_show,1):min(fr+n_show,total_frames);
    gt_h = subplot('Position',gtplotpos);
    hold off
    plot([fr fr],[-100 100],'-k')
    hold on
    if ~isempty(actions.gt)
        inds = find(actions.gt.idxmap(active_fly,curr_beh,framerange));
        noneinds = setdiff(1:numel(framerange),inds);
        % plot non-actions
        plot(noneinds+framerange(1)-1, zeros(size(noneinds)), '.', 'color', [.5 .5 .5])
        % plot actions
        plot(inds+framerange(1)-1, zeros(size(inds)), '.', 'color', actions.beh_colors(curr_beh,:),'markersize',20)
    else
        set(gt_h,'color',subpl_clr); box on
    end
    xlim([(fr-n_show) (fr+n_show)]);
    set(gt_h,'XTick',[]);
    set(gt_h,'YTick',[]);
    
    % update pred
    pred_h = subplot('Position',predplotpos);
    hold off
    plot([fr fr],[-100 100],'-k')
    hold on
    if ~isempty(actions.pred)
        inds = find(actions.pred.idxmap(active_fly,curr_beh,framerange));
        noneinds = setdiff(1:numel(framerange),inds);
        % plot non-actions
        plot(noneinds+framerange(1)-1, zeros(size(noneinds)), '.', 'color', [.5 .5 .5])
        % plot actions
        plot(inds+framerange(1)-1, zeros(size(inds)), '.', 'color', actions.beh_colors(curr_beh,:),'markersize',20)
    else
        set(pred_h,'color',subpl_clr); box on
    end    
    xlim([(fr-n_show) (fr+n_show)]);
    set(pred_h,'XTick',[]);
    set(pred_h,'YTick',[]);
    
    % update features
    feat_h = subplot('Position',featplotpos);
    hold off
    plot([fr fr],[-100 100],'-k')
    hold on
    ytick = [];
    if ~isempty(feat)
        featvec = feat.data(:,:,curr_feat);
        plot(framerange,featvec(active_fly,framerange),'-','color',clrs(active_fly,:));hold on;
        hold off;
        minfeat = nanmin(featvec(:));
        maxfeat = nanmax(featvec(:));
        medfeat = nanmedian(featvec(:));
        ylim([minfeat maxfeat]);
        range = maxfeat-minfeat;
        ytick = [minfeat+range*.1 medfeat minfeat+range*.9];
    end
    xlim([(fr-n_show) (fr+n_show)]);
    set(feat_h,'XTick',[]);
    set(feat_h,'YTick',unique(round(ytick*10)/10));    
    
    updating = 0;
end

function play()
    set(video_h(6),'Visible','off');
    orig_time = clock; 
    orig_frame = curr_frame;
    while do_play
        curr_time = clock;
        time_diff = etime(curr_time,orig_time);
        frame_diff = round(time_diff*FPS);
        curr_frame = orig_frame + frame_diff;
        if curr_frame > total_frames-1
            do_play = 0;
            curr_frame = total_frames-1;
            set(video_h(2),'string','play')
            set(video_h(2),'backgroundcolor',[.3 .7 .4])
        end  
        update_display(curr_frame);
        prev_frame=curr_frame-1;
    end
end

function goto_bout(id)
    active_bout(active_fly) = max(min(id,size(actions.curr_bouts,1)),0);
    set(behavior_h(6),'String',num2str(active_bout(active_fly)))
    if active_bout(active_fly) > 0
        curr_frame = actions.curr_bouts(active_bout(active_fly),1)-1;
        update_display(curr_frame);
    end    
end

function set_bouts()
    if curr_beh_type == 1 % ground truth
        actions.curr_bouts = actions.gt.bouts{active_fly,curr_beh};
    elseif curr_beh_type == 2 % prediction 
        actions.curr_bouts = actions.pred.bouts{active_fly,curr_beh};
    elseif curr_beh_type == 3 % false negatives
        actions.curr_bouts = actions.gt.bouts{active_fly,curr_beh}(actions.fn{active_fly,curr_beh},:);
    elseif curr_beh_type == 4 % false positives
        actions.curr_bouts = actions.pred.bouts{active_fly,curr_beh}(actions.fp{active_fly,curr_beh},:);
    end
    if isempty(actions.curr_bouts)
        actions.curr_bouts = zeros(0,2);
    end
    actions.curr_frames = zeros(1,total_frames);
    for b=1:size(actions.curr_bouts,1)
        frames = actions.curr_bouts(b,1):actions.curr_bouts(b,2);
        actions.curr_frames(frames) = 1;
    end    
    % Set number of behaviors
    set(behavior_h(9),'string',num2str(size(actions.curr_bouts,1)));
    
end


%% READ/WRITE functions
function open_videofile(path,video_file)
    % load video
    try 
        vinfo_ = video_open(fullfile(path,video_file));
        video_read_frame(vinfo_,0);
    catch
        warndlg('Could not load video file','Warning')
        return; 
    end
    % set new video
    init_video;
    vinfo = vinfo_;
    video_dir = path;
    % get video information
    FPS = vinfo.vidobj.FrameRate;   
    total_frames = vinfo.n_frames;
    set(video_h(5),'string',num2str(total_frames))
    set(video_h(1),'Min',0,'Max',total_frames-1,'value',0,'sliderstep',[1/total_frames .1]); 
    set(video_h(3),'backgroundcolor',figclr)
    set(label_h,'string',fullfile(path,video_file))
    % enable video viewing components
    for j=1:numel(video_h)
        set(video_h(j),'enable','on')
    end
    % enable video dependent compoenents
    set(trk_vid_h,'enable','on')
    % plot first frame   
    update_display(curr_frame)
    
    % load trk file
    [~,rawname] = fileparts(video_file);
    trk_file = [rawname '_track.mat'];
    if exist(fullfile(path,trk_file),'file')
       open_trkfile(path,trk_file) 
    end
    axis_lim = [1 vinfo.sy 1 vinfo.sx];
    
    % load annotation file
    anno_file = [rawname '_actions.mat'];
    if exist(fullfile(path,anno_file),'file')
        load_labelfile(path,anno_file,'gt')
    end
end

function open_trkfile(path,trk_file)
    % load trk file
    try
        D = load(fullfile(path,trk_file)); trk_ = D.trk;
    catch
        warndlg('Could not load trk file','Warning')
        return
    end
    % reset interface
    if isempty(vinfo)
        init_video;
    else
        init_track;
    end
    % enable track viewing components
    for j=1:numel(track_h)
        set(track_h(j),'enable','on')
    end
    % set new track
    trk = trk_;
    for j=1:size(trk.data,1)
        trk.obj_list(j,:) = j;
    end    
    files.trk = trk_file;
    files.path = path;
    set(gtm_h,'enable','on')
    set(predm_h,'enable','on')
    % define object colors
    n_objs = size(trk.data,1);
    curr_flies = 1:size(trk.data,1);

    clrs = zeros(n_objs,3);
    f_count = 0;    
    flies = curr_flies;
    if numel(flies) > 3
        tmp = jet(size(trk.data,1));
    else
        tmp = [.3 .5 1; 1 .3 .3; .3 .8 .6];
    end
    for f=1:numel(flies)
        f_count = f_count + 1;
        clrs(f_count,:) = tmp(f,:);            
    end

    % load feature file
    try
        featfile = [trk_file(1:end-10) '_feat.mat'];
        if exist(fullfile(path,featfile),'file')
            D = load(fullfile(path,featfile)); feat = D.feat;
            for j=1:numel(featl_h)
                set(featl_h(j),'enable','on')
            end
            featstr = feat.names{1};
            for j=2:numel(feat.names)
                featstr = [featstr '|' feat.names{j}];
            end
            set(featl_h(2),'string',featstr)
        end
        files.feat = featfile;
    catch
        warndlg('Could not load feat file','Warning')
    end   
    
    if isempty(vinfo)
        % set necessary video variables
        video_dir = path;
        FPS = 30;
        total_frames = size(trk.data,2);
        set(video_h(5),'string',num2str(total_frames))
        set(video_h(1),'Min',0,'Max',total_frames-1,'value',0,'sliderstep',[1/total_frames .1]); 
        set(video_h(3),'backgroundcolor',figclr)
        set(label_h,'string',fullfile(path,trk_file))
        % enable video viewing components
        for j=1:numel(video_h)
            set(video_h(j),'enable','on')
        end       
        sx = ceil(max(max(trk.data(1,:,2)),max(trk.data(2,:,2))));
        sy = ceil(max(max(trk.data(1,:,1)),max(trk.data(2,:,1))));
        axis_lim = [1 sy 1 sx];
        
        % load annotation file
        rawname = trk_file(1:end-10);
        anno_file = [rawname '_actions.mat'];
        if exist(fullfile(path,anno_file),'file')
            load_labelfile(path,anno_file,'gt')
        end
    end
   
    update_display(curr_frame)
end

function load_label(gt_pred)
    [label_file, path] = uigetfile({'*.txt;*.label;*.mat'},'Select track file',video_dir);
    if ~label_file
        return
    end
    % load label file
    load_labelfile(path,label_file,gt_pred);
end

function load_labelfile(path,label_file,gt_pred)
    try     
        D = load(fullfile(path,label_file));
        behaviors = D.behs;
        behaviors = setdiff(behaviors,{'none','other'});
        if ~isempty(actions.beh_labels)
            behaviors = [actions.beh_labels setdiff(behaviors,actions.beh_labels)];
        end
        n_flies = 2;
        n_behs = numel(behaviors);
        bouts = cell(n_flies,n_behs);
        idxmap = zeros(n_flies,n_behs,total_frames);
        for f=1:n_flies
            for b=1:n_behs
                beh_idx = find(strcmp(D.behs,behaviors{b}));
                if beh_idx > 0
                    aaa=D.bouts{f,beh_idx};
                    if (size(aaa,2)~=3)
                        bouts{f,b} = horzcat(aaa,zeros(size(aaa,1),1));
                    else
                        bouts{f,b} =aaa;
                    end
                    for idx = 1:size(bouts{f,b},1)
                        idxmap(f,b,bouts{f,b}(idx,1):bouts{f,b}(idx,2)) = idx;
                    end
                end
            end
        end
        files.labelfile = fullfile(path,[label_file(1:end-12) '_actions.mat']);
    catch
        warndlg('File is of unknown format','Warning')
        return
    end
    actions.beh_labels = behaviors;
    actions.beh_colors = jet(numel(actions.beh_labels));    
    
    % Set behavior list
    beh_string = actions.beh_labels{1};
    for b=2:numel(actions.beh_labels)
        beh_string = [beh_string '|' actions.beh_labels{b}];
    end
    set(behavior_h(2),'string',beh_string);
    
    % Set behavior type list
    have_gt = ~isempty(actions.gt) || strcmp(gt_pred,'gt');
    have_pred = ~isempty(actions.pred) || strcmp(gt_pred,'pred');
    behl_string = '';
    if have_gt && have_pred
        behl_string = 'ground truth|prediction|false negatives|false positives';
    elseif have_gt
        behl_string = 'ground truth';
    elseif have_pred
        behl_string = 'prediction';
    end
    set(behavior_h(4),'string',behl_string);
    if strcmp(gt_pred,'gt')
        files.gt = label_file;
        actions.gt.bouts = bouts;
        actions.gt.idxmap = idxmap;
        actions.gt.idxmap_orig = idxmap;
        set(gtl_h,'enable','on')    
        if ~isempty(actions.pred) && size(actions.gt.bouts,2) > size(actions.pred.bouts,2)
            pred.bouts = cell(size(actions.gt.bouts));
            pred.idxmap = zeros(size(actions.gt.idxmap));
            for ii=1:size(actions.pred.bouts,1)
                for jj=1:size(actions.pred.bouts,2)
                    pred.bouts{ii,jj} = actions.pred.bouts{ii,jj};
                end
            end
            actions.pred.bouts = pred.bouts;
            pred.idxmap(:,1:size(actions.pred.bouts,2),:) = actions.pred.idxmap;
            actions.pred.idxmap = pred.idxmap;
        end
        % try loading unsaved behavior annotations
        [~,filename] = fileparts(label_file);
        labelsname = ['tmp_' filename '_edits.mat'];
        labelsfile = fullfile(path,labelsname);
        files.tmplabelfile = labelsfile;
        if exist(labelsfile,'file')
            D = load(labelsfile); actions = D.actions;
            set(behavior_h(15),'enable','on');
        end            
    else
        files.pred = label_file;
        actions.pred.bouts = bouts;
        actions.pred.idxmap = idxmap; 
        set(predl_h,'enable','on')    
        if ~isempty(actions.gt) && size(actions.pred.bouts,2) > size(actions.gt.bouts,2)
            gt.bouts = cell(size(actions.pred.bouts));
            gt.idxmap = zeros(size(actions.pred.idxmap));
            for ii=1:size(actions.gt.bouts,1)
                for jj=1:size(actions.gt.bouts,2)
                    gt.bouts{ii,jj} = actions.gt.bouts{ii,jj};
                    gt.idxmap(ii,jj,:) = actions.gt.idxmap(ii,jj,:);
                end
            end
            actions.gt.bouts = gt.bouts;
            actions.gt.idxmap = gt.idxmap;
            actions.gt.idxmap_orig = gt.idxmap;
        end        
    end
    for j=1:numel(behavior_h)
        set(behavior_h(j),'enable','on')
    end
  
    % Find false negatives and positives
    if have_gt && have_pred
        actions.fn = cell(n_flies,n_behs);
        actions.fp = cell(n_flies,n_behs);
        for f=1:n_flies
            for b=1:numel(actions.beh_labels)
                % loop through gt bouts and see if there is an overlapping pred bout
                if numel(actions.gt.bouts{f,b}) > 0
                    for j=1:size(actions.gt.bouts{f,b},1)
                        gt_frames = actions.gt.bouts{f,b}(j,1):actions.gt.bouts{f,b}(j,2);
                        pred_frames = find(actions.pred.idxmap(f,b,gt_frames)>0);
                        overlap = numel(pred_frames)/numel(gt_frames);
                        if overlap < actions.overlapTHRESH
                            actions.fn{f,b}(end+1) = j;
                        end
                    end
                end
                % loop through pred bouts and see if there is an overlapping gt bout
                if numel(actions.pred.bouts{f,b})
                    for j=1:size(actions.pred.bouts{f,b},1)
                        pred_frames = actions.pred.bouts{f,b}(j,1):actions.pred.bouts{f,b}(j,2);
                        gt_frames = find(actions.gt.idxmap(f,b,pred_frames)>0);
                        overlap = numel(gt_frames)/numel(pred_frames);
                        if overlap < actions.overlapTHRESH
                            actions.fp{f,b}(end+1) = j;
                        end
                    end
                end
            end
        end
    end
    
    % Set current bouts 
    if ~isempty(actions.gt)
        curr_beh_type = 1;
    else
        curr_beh_type = 2;
    end
    set_bouts();    
    
    if isempty(active_bout)
        active_bout = zeros(1,n_flies);
    end
    set(behavior_h(6),'String',num2str(active_bout(active_fly)))
    update_display(curr_frame);
end


%% CALLBACK functions
function open_video(hObj,event) %#ok<INUSD>
    [video_file, path] = uigetfile({'*.seq;*.wmv;*.mov;*.mp4;*.avi;*.fmf;*.ufmf'},'Select video file',video_dir);
    if ~video_file
        return
    end
    open_videofile(path,video_file);
end

function open_trk(hObj,event) %#ok<INUSD>
    % get track file
    [trk_file, path] = uigetfile('*track.mat','Select track file',video_dir);
    if ~trk_file
        return
    end
    open_trkfile(path,trk_file);
end

function open_trk_novid(hObj,event) %#ok<INUSD>
    % get track file
    vinfo = [];
    set(trk_vid_h,'enable','off')
    [trk_file, path] = uigetfile('*track.mat','Select track file',video_dir);
    if ~trk_file
        return
    end
    open_trkfile(path,trk_file);
end

function open_gtlabel(hObj,event) %#ok<INUSD>
    % first check if there are unsaved changes
    load_label('gt');
end

function open_predlabel(hObj,event) %#ok<INUSD>
    load_label('pred');
end

function go_back_to_frame(hObj,event) %#ok<INUSD>
    curr_frame=curr_frame+1;
    update_display(prev_frame-1)
    aux_frame=prev_frame;
    prev_frame=curr_frame;
    curr_frame=aux_frame-1;

end

function play_vid(hObj,event) %#ok<INUSD>
    if strcmp(get(hObj,'string'),'play')
        do_play = true;
        set(hObj,'string','pause')
        set(hObj,'backgroundcolor',[1 .5 .5])
        play();
    else
        do_play = false;
        set(hObj,'string','play')
        set(hObj,'backgroundcolor',[.3 .7 .4])
    end
end

function play_bout(hObj,~,caller)
    if strcmp(caller,'beh')
        if active_bout(active_fly) < 1, return; end    
        bout = actions.curr_bouts(active_bout(active_fly),1):actions.curr_bouts(active_bout(active_fly),2);
        pause_dur = 0.1;
    else
        if id_switch < 1, return; end
        bout = switches(id_switch,1):switches(id_switch,2);
        pause_dur = 0.5;
    end
    do_play = 0;
    value = get(hObj,'string');
    if strcmp(value,'play clip')
        set(hObj,'string','stop')
        set(hObj,'backgroundcolor',[1 .5 .5])
        stop_bout_play = 0;
        for f = bout
            if stop_bout_play
                break
            end
            curr_frame = f-1;
            update_display(curr_frame);
            pause(pause_dur)
        end
        set(hObj,'string','play clip')
        set(hObj,'backgroundcolor',[.3 .7 .4])    
    else
        set(hObj,'string','play clip')
        set(hObj,'backgroundcolor',[.3 .7 .4])    
        stop_bout_play = 1;
    end
    set(hObj,'enable','on')
end

function vid_slider(hObj,event) %#ok<INUSD>
    prev_frame=str2double(get(video_h(3),'string'));
    if strcmp(get(video_h(2),'string'),'pause')
        do_play = false;
        set(video_h(2),'string','play')
        set(video_h(2),'backgroundcolor',[.3 .7 .4])
    end
    curr_frame = round(get(hObj,'value'));
    update_display(curr_frame)
%     if (abs(curr_frame-prev_frame))>2
%         set(video_h(6),'Visible','on');
%     else
%         set(video_h(6),'Visible','off');
%     end
end

function zoomin(hObj,event) %#ok<INUSD>
    figure(fig_h)
    sub_h = subplot('Position',vidplotpos);
    tmp = get(zoom);
    if strcmp(tmp.Enable,'on') && strcmp(tmp.Direction,'in')
        zoom off
        % change in label to off
        set(zoom_h(1),'cdata',zoom_imgs{1}) 
    else
        set(zoom,'direction','in','enable','on')
        pan off
        % change in label to on
        set(zoom_h(1),'cdata',zoom_imgs{4}) 
        % change out label to off
        set(zoom_h(2),'cdata',zoom_imgs{2})
        % change pan label to off
        set(zoom_h(3),'cdata',zoom_imgs{3})
    end
end

function zoomout(hObj,event) %#ok<INUSD>
    figure(fig_h)
    sub_h = subplot('Position',vidplotpos);
    tmp = get(zoom);
    if strcmp(tmp.Enable,'on') && strcmp(tmp.Direction,'out')
        zoom off
        % change out label to off
        set(zoom_h(2),'cdata',zoom_imgs{2})
    else
        set(zoom,'direction','out','enable','on')
        pan off
        % change out label to on
        set(zoom_h(2),'cdata',zoom_imgs{5})
        % change in label to off
        set(zoom_h(1),'cdata',zoom_imgs{1}) 
        % change pan label to off
        set(zoom_h(3),'cdata',zoom_imgs{3})
    end
end

function panfunc(hObj,event) %#ok<INUSD>
    figure(fig_h)
    sub_h = subplot('Position',vidplotpos);
    tmp = get(pan);
    if strcmp(tmp.Enable,'on') 
        pan off
        % change pan label to off
        set(zoom_h(3),'cdata',zoom_imgs{3})
    else
        pan on
        zoom off
        % change pan label to on
        set(zoom_h(3),'cdata',zoom_imgs{6})
        % change in label to off
        set(zoom_h(1),'cdata',zoom_imgs{1}) 
        % change out label to off
        set(zoom_h(2),'cdata',zoom_imgs{2})
    end
end

function go_to_frame(hObj,event) %#ok<INUSD>
    value = str2num(get(hObj,'string'));
    if isempty(value), return; end
    curr_frame = value-1;
    update_display(curr_frame)
end

function set_feat(hObj,event) %#ok<INUSD>
    curr_feat = get(hObj,'value');
    update_display(curr_frame);
end

function show_all(hObj,event) %#ok<INUSD>
    value = get(hObj,'value');
    if value == 1
        plt.trk = 1;
        plt.ellipse = 1;
        plt.wings = 1;
        for j=1:3
            set(track_h(1+j),'value',1)
        end
    else
        plt.trk = 0;
        plt.ellipse = 0;
        plt.wings = 0;
        for j=1:3
            set(track_h(1+j),'value',0)
        end
    end
    if ~do_play, update_display(curr_frame); end
end

function show_track(hObj,event) %#ok<INUSD>
    value = get(hObj,'value');
    if value == 1
        plt.trk = 1;
    else
        plt.trk = 0;
        set(track_h(1),'value',0)
    end
    if plt.trk==1 && plt.ellipse==1 && plt.wings==1
        set(track_h(1),'value',1)
    end
    if ~do_play, update_display(curr_frame); end
end

function show_ellipse(hObj,event) %#ok<INUSD>
    value = get(hObj,'value');
    if value == 1
        plt.ellipse = 1;
    else
        plt.ellipse = 0;
        set(track_h(1),'value',0)
    end
    if plt.trk==1 && plt.ellipse==1 && plt.wings==1
        set(track_h(1),'value',1)
    end
    if ~do_play, update_display(curr_frame); end
end

function show_wings(hObj,event) %#ok<INUSD>
    value = get(hObj,'value');
    if value == 1
        plt.wings = 1;
    else
        plt.wings = 0;
        set(track_h(1),'value',0)
    end
    if plt.trk==1 && plt.ellipse==1 && plt.wings==1
        set(track_h(1),'value',1)
    end
    if ~do_play, update_display(curr_frame); end
end

function mouse_pressed(hObj,event) %#ok<INUSD>
    pos = get(fig_h,'CurrentPoint');
    h = [];
    
    vidlim  = [vidplotpos(1)*width vidplotpos(2)*height (vidplotpos(1)+vidplotpos(3))*width (vidplotpos(2)+vidplotpos(4))*height];
    gtlim   = [gtplotpos(1)*width gtplotpos(2)*height (gtplotpos(1)+gtplotpos(3))*width (gtplotpos(2)+gtplotpos(4))*height];
    predlim = [predplotpos(1)*width predplotpos(2)*height (predplotpos(1)+predplotpos(3))*width (predplotpos(2)+predplotpos(4))*height];
    featlim = [featplotpos(1)*width featplotpos(2 )*height (featplotpos(1)+featplotpos(3))*width (featplotpos(2)+featplotpos(4))*height];

    if isempty(axis_lim)
        return
    end
    
    axsz = axis_lim([2 4])-axis_lim([1 3])+1;
    vidlsz = vidlim(3:4)-vidlim(1:2)+1;    
    if axsz(2)/axsz(1) < vidlsz(2)/vidlsz(1)
        % scaled vertically
        diffr = (vidlsz(2)/vidlsz(1) - axsz(2)/axsz(1)) * vidlsz(2);
        vidlim(2) = vidlim(2) + diffr/2;
        vidlim(4) = vidlim(4) - diffr/2;
    elseif axsz(2)/axsz(1) > vidlsz(2)/vidlsz(1)
        % scaled horizontally
        diffr = (axsz(2)/axsz(1) - vidlsz(2)/vidlsz(1)) * vidlsz(1);
        vidlim(1) = vidlim(1) + diffr/2;
        vidlim(3) = vidlim(3) - diffr/2;       
    end
    
    if pos(1) >= vidlim(1) && pos(1) < vidlim(3) && ...
       pos(2) >= vidlim(2) && pos(2) < vidlim(4)
        if isempty(trk), return; end
        h = sub_h;
        
        vidsz = axis_lim([2 4]);
        vidlsz = vidlim(3:4)-vidlim(1:2)+1;
        
        orig_axsz = axsz(2) * vidsz(1)/vidsz(2);
        diffr = [(orig_axsz - axsz(1)) / orig_axsz 0];
        shift_figpix = diffr .* vidlsz / 2 + vidlim(1:2);
        len_figpix = (1-diffr).*vidlsz;

        pos = (pos - shift_figpix)./len_figpix;
    elseif pos(1) >= gtlim(1) && pos(1) < gtlim(3) && ...
           pos(2) >= gtlim(2) && pos(2) < gtlim(4)
        h = gt_h;
        pos = (pos-gtlim(1:2)+1)./(gtlim(3:4)-gtlim(1:2));
    elseif pos(1) >= predlim(1) && pos(1) < predlim(3) && ...
           pos(2) >= predlim(2) && pos(2) < predlim(4)
        h = pred_h;
        pos = (pos-predlim(1:2)+1)./(predlim(3:4)-predlim(1:2));
    elseif pos(1) >= featlim(1) && pos(1) < featlim(3) && ...
           pos(2) >= featlim(2) && pos(2) < featlim(4)
        h = feat_h;
        pos = (pos-featlim(1:2)+1)./(featlim(3:4)-featlim(1:2));
    end
    if isempty(h), return; end
    pos(2) = 1-pos(2);
    roi = axis(h);
    pos = pos.*(roi([2 4])-roi([1 3])) + roi([1 3]);
    
    if h==sub_h
        % check which fly was clicked, if any
        min_dist = inf;
        closest_obj = 0;
        for o=1:size(trk.data,1)
            sq_dist = (pos(1)-trk.data(o,curr_frame+1,1))^2+(pos(2)-trk.data(o,curr_frame+1,2))^2;
            if sq_dist < min_dist
                min_dist = sq_dist;
                closest_obj = o;
            end
        end
        if sqrt(min_dist) <= trk.data(closest_obj,curr_frame+1,4)/2
            active_fly = closest_obj;
            if ~isempty(actions.gt) || ~isempty(actions.pred)
                set_bouts()
                set(behavior_h(6),'String',num2str(active_bout(active_fly)))
            end
            update_display(curr_frame);
        end
    else
        % update frame position
        fr = min(total_frames,max(1,round(pos(1))));
        curr_frame = fr-1;
        update_display(curr_frame);
    end
end

function bout_up(hObj,event) %#ok<INUSD>
    active_bout(active_fly) = min(active_bout(active_fly)+1,size(actions.curr_bouts,1));
    goto_bout(active_bout(active_fly))
end

function bout_down(hObj,event) %#ok<INUSD>
    active_bout(active_fly) = max(active_bout(active_fly)-1,0);
    goto_bout(active_bout(active_fly))
end

function set_bout(hObj,event) %#ok<INUSD>
    active_bout(active_fly) = str2num(get(hObj,'String'));
    goto_bout(active_bout(active_fly))
end

function set_beh(hObj,event) %#ok<INUSD>
    val = get(hObj,'value');
    if val==curr_beh
        return
    end
    curr_beh = val;
    set_bouts()
    active_bout(:) = 0;
    active_bout(active_fly) = min(1,size(actions.curr_bouts,1));
    set(behavior_h(6),'String',num2str(active_bout(active_fly)))
    update_display(curr_frame);
end

function set_beh_type(hObj,event) %#ok<INUSD>
    val = get(hObj,'value');
    if isempty(actions.gt) || isempty(actions.pred) || curr_beh_type == val
        return
    end
    curr_beh_type = val;
    set_bouts()
    active_bout(:) = 0;
    active_bout(active_fly) = min(1,size(actions.curr_bouts,1));
    set(behavior_h(6),'String',num2str(active_bout(active_fly)))
    update_display(curr_frame);    
end

function myclose(hObj,event) %#ok<INUSD>
    % close window
    delete(fig_h)
end

end
