package jetbrains.mps.baseLanguage.unitTest.execution.tool;

/*Generated by MPS */

import javax.swing.table.TableModel;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import javax.swing.event.TableModelListener;
import jetbrains.mps.baseLanguage.unitTest.execution.TestNodeKey;
import java.util.Map;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import jetbrains.mps.baseLanguage.unitTest.execution.client.TestRunState;
import jetbrains.mps.baseLanguage.unitTest.execution.client.TestStateListener;
import jetbrains.mps.baseLanguage.unitTest.execution.client.TestStateAdapter;
import jetbrains.mps.baseLanguage.unitTest.execution.TestNodeEvent;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.baseLanguage.unitTest.execution.TerminationTestEvent;
import java.util.Iterator;
import jetbrains.mps.baseLanguage.unitTest.execution.TestMethodNodeKey;
import jetbrains.mps.baseLanguage.unitTest.execution.client.ITestNodeWrapper;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.baseLanguage.unitTest.execution.TestCaseNodeKey;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.application.ApplicationManager;
import javax.swing.event.TableModelEvent;

public class StatisticsTableModel implements TableModel {
  private static final List<String> TEST_COLUMNS = ListSequence.fromListAndArray(new ArrayList<String>(), "Test", "Time elapsed", "Usage Delta", "Usage Before", "Usage After", "Results");
  private final List<TableModelListener> myListeners = ListSequence.fromList(new ArrayList<TableModelListener>());
  private List<TestStatisticsRow> myRows;
  private List<TestStatisticsRow> myFilteredRows = ListSequence.fromList(new ArrayList<TestStatisticsRow>());
  private TestNodeKey myFilter = null;
  private final Map<TestNodeKey, TestStatisticsRow> myNodeKey2RowMap = MapSequence.fromMap(new HashMap<TestNodeKey, TestStatisticsRow>());
  private final TestRunState myState;
  private final TestStateListener myTestStateListener;

  public StatisticsTableModel(TestRunState state) {
    myState = state;
    initFromTests(state.getTestsMap());
    myTestStateListener = new TestStateAdapter() {
      @Override
      public void onTestStart(TestNodeEvent event) {
        TestMethodRow row = findRowForEvent(event);
        if (row != null) {
          row.setStartTime(event.getRawEvent().getTime());
          row.setUsageBefore(event.getRawEvent().getMemoryUsage());
          fireTableChanged();
        }
      }

      @Nullable
      private TestMethodRow findRowForEvent(TestNodeEvent event) {
        return getRow(event.getTestKey());
      }

      @Override
      public void onTestFinish(TestNodeEvent event) {
        TestMethodRow row = findRowForEvent(event);
        if (row != null) {
          row.setFinishTime(event.getRawEvent().getTime());
          row.setUsageAfter(event.getRawEvent().getMemoryUsage());
          if (row.getFailed() == 0 && row.getErrored() == 0) {
            row.setSucceed();
          }
          fireTableChanged();
        }
      }

      @Override
      public void onTestFailure(TestNodeEvent event) {
        TestMethodRow row = findRowForEvent(event);
        if (row != null) {
          row.setErrored();
          fireTableChanged();
        }
      }

      @Override
      public void onTestAssumptionFailure(TestNodeEvent event) {
        TestMethodRow row = findRowForEvent(event);
        if (row != null) {
          row.setFailed();
          fireTableChanged();
        }
      }


      @Override
      public void onTermination(TerminationTestEvent event) {
        {
          Iterator<TestMethodNodeKey> notRunTest_it = ListSequence.fromList(event.getNotRanTests()).iterator();
          TestMethodNodeKey notRunTest_var;
          while (notRunTest_it.hasNext()) {
            notRunTest_var = notRunTest_it.next();
            onLooseTest(notRunTest_var, event.isTerminatedCorrectly());
          }
        }
      }

      public void onLooseTest(TestNodeKey notRunTest, boolean isTerminatedCorrectly) {
        TestMethodRow row = getRow(notRunTest);
        if (row != null) {
          if (!(isTerminatedCorrectly)) {
            row.setErrored();
          }
          fireTableChanged();
        }
      }
    };

    myState.addListener(myTestStateListener);
  }

  public void dispose() {
    myState.removeListener(myTestStateListener);
  }

  private void initFromTests(Map<ITestNodeWrapper, List<ITestNodeWrapper>> tests) {
    myRows = ListSequence.fromList(new ArrayList<TestStatisticsRow>());
    TotalRow totalRow = new TotalRow();
    ListSequence.fromList(myRows).addElement(totalRow);
    MapSequence.fromMap(myNodeKey2RowMap).clear();
    for (ITestNodeWrapper testCase : SetSequence.fromSet(MapSequence.fromMap(tests).keySet())) {
      TestCaseNodeKey testCaseKey = new TestCaseNodeKey(testCase);
      TestCaseRow testCaseRow = new TestCaseRow(testCaseKey);
      totalRow.addRow(testCaseRow);
      ListSequence.fromList(myRows).addElement(testCaseRow);
      MapSequence.fromMap(myNodeKey2RowMap).put(testCaseKey, testCaseRow);
      for (ITestNodeWrapper testMethod : ListSequence.fromList(MapSequence.fromMap(tests).get(testCase))) {
        TestMethodNodeKey methodKey = new TestMethodNodeKey(testMethod);
        TestMethodRow testMethodRow = new TestMethodRow(methodKey);
        testCaseRow.addRow(testMethodRow);
        ListSequence.fromList(myRows).addElement(testMethodRow);
        MapSequence.fromMap(myNodeKey2RowMap).put(methodKey, testMethodRow);
      }
    }
    filter();
  }


  @Nullable
  public TestMethodRow getRow(@NotNull TestNodeKey testNodeKey) {
    TestStatisticsRow row = MapSequence.fromMap(myNodeKey2RowMap).get(testNodeKey);
    if (row instanceof TestMethodRow) {
      return (TestMethodRow) row;
    }
    return null;
  }

  private void fireTableChanged() {
    ApplicationManager.getApplication().invokeLater(new Runnable() {
      @Override
      public void run() {
        for (TableModelListener listener : ListSequence.fromList(myListeners)) {
          listener.tableChanged(new TableModelEvent(StatisticsTableModel.this));
        }
      }
    });
  }

  @Override
  public int getRowCount() {
    return ListSequence.fromList(myFilteredRows).count();
  }

  @Override
  public Object getValueAt(int rowIndex, int columnIndex) {
    return ListSequence.fromList(myFilteredRows).getElement(rowIndex);
  }

  public void setFilter(@Nullable TestNodeKey key) {
    myFilter = key;
    filter();
  }

  private void filter() {
    myFilteredRows = ListSequence.fromList(myRows).where((it) -> it.matches(myFilter)).toList();
    fireTableChanged();
  }

  @Override
  public int getColumnCount() {
    return ListSequence.fromList(TEST_COLUMNS).count();
  }

  @Override
  public String getColumnName(int columnIndex) {
    return ListSequence.fromList(TEST_COLUMNS).getElement(columnIndex);
  }

  @Override
  public Class<?> getColumnClass(int columnIndex) {
    return TestStatisticsRow.class;
  }

  @Override
  public boolean isCellEditable(int rowIndex, int columnIndex) {
    return false;
  }

  @Override
  public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
  }

  @Override
  public void addTableModelListener(TableModelListener listener) {
    ListSequence.fromList(myListeners).addElement(listener);
  }

  @Override
  public void removeTableModelListener(TableModelListener listener) {
    ListSequence.fromList(myListeners).removeElement(listener);
  }
}
