/*******************************************************************************
 * Copyright 2017
 * Language Technology Lab
 * University of Duisburg-Essen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package de.unidue.ltl.evaluation.measure.agreement;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.dkpro.statistics.agreement.InsufficientDataException;
import org.dkpro.statistics.agreement.coding.CodingAnnotationStudy;
import org.dkpro.statistics.agreement.coding.WeightedKappaAgreement;
import org.dkpro.statistics.agreement.distance.IntervalDistanceFunction;

import de.tudarmstadt.ukp.dkpro.core.api.frequency.util.FrequencyDistribution;
import de.unidue.ltl.evaluation.EvaluationData;
import de.unidue.ltl.evaluation.EvaluationEntry;
import de.unidue.ltl.evaluation.measure.categorial.Accuracy;

public class QuadraticallyWeightedKappa<T extends Number>
    extends AgreementMeasure<T>
{

    boolean didCalculate = false;
    double calculateAgreement;

    public QuadraticallyWeightedKappa(EvaluationData<T> data)
    {
        super(data);
    }

    void calculate()
    {
        if (didCalculate) {
            return;
        }
        ArrayList<Integer> gold = new ArrayList<Integer>();
        ArrayList<Integer> predictions = new ArrayList<Integer>();
        Set<Integer> labels = new HashSet<Integer>();
        Iterator<EvaluationEntry<T>> iter = data.iterator();
        while (iter.hasNext()) {
        	EvaluationEntry<T> e = iter.next();
        	gold.add((int) Math.round(e.getGold().doubleValue()));
        	predictions.add((int) Math.round(e.getPredicted().doubleValue()));
        	labels.add((int) Math.round(e.getGold().doubleValue()));
        	labels.add((int) Math.round(e.getPredicted().doubleValue()));
        }
        calculateAgreement = getKappa(gold.toArray(new Integer[gold.size()]), predictions.toArray(new Integer[predictions.size()]), labels.toArray(new Integer[0]));

        didCalculate = true;
    }

    @Override
    public double getAgreement()
    {
        if (!didCalculate) {
            calculate();
        }

        return calculateAgreement;
    }
    
  

    public static double getKappa(Integer[] ratingsA, Integer[] ratingsB, Integer... categories) {
        if (ratingsA.length != ratingsB.length) {
            throw new IllegalArgumentException("Rating vectors need to be of equal size.");
        }
        
        if (Arrays.equals(ratingsA,ratingsB)) {
        	return 1.0;
        }
    
        ConfusionMatrix confMatrix = new ConfusionMatrix(ratingsA, ratingsB, categories);

        int nrofRatings = categories.length;
        int nrofScoredItems = ratingsA.length;

        FrequencyDistribution<Integer> freqDistA = new FrequencyDistribution<Integer>(Arrays.asList(ratingsA));
        FrequencyDistribution<Integer> freqDistB = new FrequencyDistribution<Integer>(Arrays.asList(ratingsB));
        
        double numerator = 0.0;
        double denominator = 0.0;

        for (int outerCategory : categories) {
            for (int innerCategory : categories) {
                int nMinusOne = nrofRatings - 1;
                int distance = outerCategory - innerCategory;
                double weight = (double) (distance*distance) / (nMinusOne*nMinusOne);
                
                double expectedCount = (double) (freqDistA.getCount(outerCategory) * freqDistB.getCount(innerCategory)) / nrofScoredItems;
                numerator += weight * confMatrix.getElement(outerCategory, innerCategory) / nrofScoredItems;
                denominator += weight * expectedCount / nrofScoredItems;
            }
        }
        return 1.0 - numerator / denominator;
    }
    
    
    
    
    
    
    
}


@@ -1025,289 +1025,8 @@
 %0D%0A%0D%0A
-import org.dkpro.statistics.agreement.InsufficientDataException;%0D%0Aimport org.dkpro.statistics.agreement.coding.CodingAnnotationStudy;%0D%0Aimport org.dkpro.statistics.agreement.coding.WeightedKappaAgreement;%0D%0Aimport org.dkpro.statistics.agreement.distance.IntervalDistanceFunction;%0D%0A%0D%0A
 impo
@@ -1203,70 +1203,8 @@
 ry;%0D
-%0Aimport de.unidue.ltl.evaluation.measure.categorial.Accuracy;%0D
 %0A%0D%0Ap
@@ -1559,37 +1559,42 @@
      %7D%0D%0A        
-Array
+%0D%0A        
 List%3CInteger%3E go
@@ -1633,21 +1633,16 @@
         
-Array
 List%3CInt
@@ -1755,17 +1755,23 @@
     
-Iterator%3C
+%0D%0A        for (
 Eval
@@ -1788,113 +1788,31 @@
 y%3CT%3E
-%3E iter = data.iterator();%0D%0A        while (iter.hasNext()) %7B%0D%0A        %09EvaluationEntry%3CT%3E e = iter.next();
+ e : data) %7B%0D%0A        %09
 %0D%0A  
@@ -2139,16 +2139,28 @@
 etKappa(
+%0D%0A        %09%09
 gold.toA
@@ -2190,17 +2190,28 @@
 ize()%5D),
- 
+%0D%0A        %09%09
 predicti
@@ -2255,17 +2255,28 @@
 ize()%5D),
- 
+%0D%0A        %09%09
 labels.t
@@ -2297,16 +2297,26 @@
 eger%5B0%5D)
+%0D%0A        
 );%0D%0A%0D%0A  
@@ -4106,51 +4106,7 @@
  %7D%0D%0A
-    %0D%0A    %0D%0A    %0D%0A    %0D%0A    %0D%0A    %0D%0A    %0D%0A
 %7D%0D%0A
-%0D%0A

Simplify a bit