% ---
% class ClipSimGraphMD
% Directed graph representing comparative clip similarities.
% EACH pair of vertices has just ONE directed edge connecting them
%
% each node represents a pair of clips,
% an edge (a,b) -> (a,c) is present if there is at least one
% users judging clip a more similar to clip b than to clip c
% 
% The Edges thus represent the (are nearer to each othen than) 
% expression
% ---

classdef ClipSimGraphMD < ClipSimGraph & handle

    
properties

    % ---
    % History of edge weights, is a nodes x nodes cell.
    %
    % E_hist(i,j) = m x 2 Matrix containing
    % Pair of information for each set [votes votes_complete]
    hE; % History of edge weights
end

% ---
%  the methods
% ---
methods
        
% constructor of the graph
function G = ClipSimGraphMD(comparison, comparison_ids)
    
    if nargin == 0
        % ---
        % empty constructor
        % ---
        
    elseif nargin == 1
        % todo: copy graph 
        
    elseif nargin == 2


        % ---
        % handle automatic or manual input
        % ---
        for i = 1:size(comparison,1)

            % get clips and votes
            clips = comparison_ids(comparison(i,1:3));
            votes = comparison(i,4:6);

            % for each triplet position create an edge reflecting the 
            % absolute and relative voting for this position

            % ---
            % NOTE: we index 0 - 2 to ease the mod
            % calculaton for the remaining indices
            % ---
            for v = 0:2

                % ---
                % has at least one user voted this clip out?
                % If so, treat it as an outlier and determine score
                % ---
                if votes(v+1) > 0

                    a = mod(v+1, 3)+1;
                    b = mod(v+2, 3)+1;
                    c = v+1;


                    % ---
                    % every outlier vote results in two
                    % dissimilarity equations, favored by 
                    % the people who voted for the outlier
                    % ---

                    % edge 1     
                    add_edge(G, clips(a), clips(b), clips(c), votes(v+1), sum(votes));

                    % edge 2
                    add_edge(G, clips(b), clips(a), clips(c), votes(v+1), sum(votes));
                end
            end

        end
    end
    
% end constructor function
end

% adds a node stating clip a is more near %
% to clip b then clip c
function Vid = add_node(G, a, b)

    Vid = add_node@ClipSimGraph(G, a, b);
    
    G.hE{Vid,Vid} = [];

end

function remove_node(G, a, b)
	
    if nargin == 3
        
        V1 = G.node(a, b);
    elseif nargin == 2
        
        V1 = a;
    end
    
    if ~isempty(Vid)

        % ---
        % look for edges connected to Vid
        % and delete their histograms
        % ---
        G.hE(:,Vid) = [];
        G.hE(Vid,:) = [];

        % ---
        % TODO: Efficiently Remove the actual node from the
        % GRAPH
        % ---
        G.Vclips(Vid,:) = [];

    end
end

% ---
% add an edge saying sim(a,b) > sim(a,c)
% ---
function add_edge(G, a, b, c, votes, allvotes)
    
    V1 = add_node(G, a, b);
    V2 = add_node(G, a, c);
 
    % ---
    % save new weight values into histogram
    % ---
    if isempty(G.hE(V1, V2))

        G.hE{V1,V2} = [votes allvotes];
    else
        G.hE{V1,V2} = [G.hE{V1,V2}; votes allvotes];
    end
    
    % ---
    % update Edges
    % ---
    G.update_E(a, b, c);
    
end

function remove_edge(G, a, b, c)

    if nargin == 4
        
        V1 = G.node(a, b);
        V2 = G.node(a, c);
    elseif nargin == 3
        
        V1 = a;
        V2 = b;
    end
    
    if ~isempty(V1) && ~isempty(V2)

        % set Edge to zero
        G.hE{V1, V2} = [];
    else

        error 'nodes not found';
    end
end 

% ---
% updates the edge weight given 3 clip ids or 
% two nodes, based on the edges history
%
% The specified (V1,V2) and the symmetric edges' (V2,V1) weights
% are evaluated and the stronger edge will get 
% the excessive weight while the loosing edge 
% will be deleted
% ---
function update_E(G, a, b, c)
%  update_E(G, a, b, c)
%  update_E(G, V1, V2)
    
    % determine the type of input parameters
    if nargin == 4
        
        V1 = G.node(a, b);
        V2 = G.node(a, c);
    elseif nargin == 3
        
        V1 = a;
        V2 = b;
    end
    
    % ---
    % calculate weighted sum for specified edge
    % and  for symmetric edge
    % ---
    thisw = unbal_edge_weight(G, V1, V2);
    symmw = unbal_edge_weight(G, V2, V1);

    % ---
    % the two //competing// weights are now balanced
    % ---
    w = thisw - symmw;
    
    % ---
    % set both edges
    % ---
    G.E(V1,V2) = max(w,0);
    G.E(V2,V1) = -min(w,0);
    
%     if w >= 0
%         G.E(V1,V2) = w;
%         G.E(V2,V1) = 0;
%     elseif w < 0
%         G.E(V1,V2) = 0;
%         G.E(V2,V1) = -w;
%     end
end

end

methods (Hidden)
    
    % ---
    % This is the core function of this Graph. 
    % it allows for the calculation of a single Edge,
    % before it is balanced with its symmetrical counteredge
    %
    % ---
    function thisw = unbal_edge_weight(G, V1, V2)
        % ---
        % trivial cases
        % ---
        if isempty(G.hE(V1, V2)) || isempty(G.hE{V1, V2})
            thisw = 0;
            return;
        end
        
        % ---
        % Evaluate the single historical entries 
        % and sum up
        % ---
        thisw = sum(G.hE{V1, V2},1);
        
        % ---
        % now divide the single votes by the number of
        % votes totally made with the option to improve 
        % this edge
        % ---
        thisw = thisw(1) /  thisw(2); 
        
    end
end
end
