#include "meshChecker.h"
#include "meshKernal.h"
#include "meshSingleton.h"
#include <vtkDataSet.h>
#include <vtkCell.h>
#include <vtkCellQuality.h>
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <math.h>
#include <vtkUnstructuredGrid.h>
#include <QDebug>

namespace MeshData
{
MeshChecker::MeshChecker()
{
    _meshData = MeshData::getInstance();
    _cellQuality = vtkSmartPointer<vtkCellQuality>::New();
    _qualityArray = vtkSmartPointer<vtkDoubleArray>::New();
    _cellQuality->SetUndefinedQuality(-1e66);
    _cellQuality->SetUnsupportedGeometry(-1e66);
}
MeshChecker::~MeshChecker()
{
    // 		const int n = _resultList.size();
    // 		for (int i = 0; i < n; ++i)
    // 		{
    // 			vtkDataSet* d = _resultList.at(i);
    // 			d->Delete();
    // 		}
    // 		_resultList.clear();
}

void MeshChecker::run()
{
    _qualityArray->SetNumberOfValues(0);
    _inputCount.clear();
    _checkedCount.clear();
    // 		const int nc = _resultList.size();
    // 		for (int i = 0; i < nc; ++i)
    // 		{
    // 			vtkDataSet* d = _resultList.at(i);
    // 			d->Delete();
    // 		}
    _resultList.clear();

    if (_measure == NONE) return;
    const int n = _meshData->getKernalCount();
    for (int i = 0; i < n; ++i)
    {
        MeshKernal* k = _meshData->getKernalAt(i);
        this->checkKernal(k);
    }
    _qualityArray->Modified();
    countResult();
}

void MeshChecker::setQualityMeasure(QualityMeasure m)
{
    _measure = m;
    _cellQuality->SetQualityMeasure(m);
}

void MeshChecker::checkKernal(MeshKernal* k)
{
    vtkDataSet* checkingDataset = k->getMeshData();
    _cellQuality->SetInputData(checkingDataset);
    _cellQuality->Update();
    vtkDataSet* res = _cellQuality->GetOutput();

    // 		vtkUnstructuredGrid* g = vtkUnstructuredGrid::New();
    // 		g->DeepCopy(res);
    _resultList.append(res);

    this->collectInfo(res);

}

void MeshChecker::collectInfo(vtkDataSet* res)
{
    vtkDataArray* qua = res->GetCellData()->GetScalars("CellQuality");

    const int n = res->GetNumberOfCells();

    for (int i = 0; i < n; ++i)
    {
        vtkCell* cell = res->GetCell(i);
        VTKCellType type = (VTKCellType)cell->GetCellType();
        ++_inputCount[type];

        double q = qua->GetComponent(i, 0);
        if (q > -1e65)
        {
            _qualityArray->InsertNextValue(q);
            _checkedCount[type].append(q);
        }
    }
}

QString MeshChecker::VTKCellTypeToString(VTKCellType type)
{
    QString stype("Undefined");

    switch (type)
    {
    case VTK_VERTEX:
        stype = "Vertex"; break;
    case VTK_POLY_VERTEX:
        stype = "PolyVertex"; break;
    case VTK_LINE:
        stype = "Line"; break;
    case VTK_POLY_LINE:
        stype = "PolyLine"; break;
    case VTK_TRIANGLE:
        stype = "Tri3"; break;
    case VTK_TRIANGLE_STRIP:
        stype = "Tri3Strip"; break;
    case VTK_POLYGON:
        stype = "Polygon"; break;
    case VTK_PIXEL:
        stype = "Pixel"; break;
    case  VTK_QUAD:
        stype = "Quad4"; break;
    case VTK_TETRA:
        stype = "Tet4"; break;
    case VTK_VOXEL:
        stype = "Voxel"; break;
    case VTK_HEXAHEDRON:
        stype = "Hex8"; break;
    case VTK_WEDGE:
        stype = "Wedge"; break;
    case VTK_PYRAMID:
        stype = "Pyramid"; break;
    case VTK_PENTAGONAL_PRISM:
        stype = "PentaPrism"; break;
    case VTK_HEXAGONAL_PRISM:
        stype = "HexaPrism"; break;


    case VTK_QUADRATIC_EDGE:
        stype = "QuaEdge"; break;
    case VTK_QUADRATIC_TRIANGLE:
        stype = "QuaTriangle"; break;
    case VTK_QUADRATIC_LINEAR_QUAD:
        stype = "QuaLinearQuad"; break;
    case VTK_QUADRATIC_QUAD:
        stype = "QuaQuad"; break;
    case VTK_BIQUADRATIC_QUAD:
        stype = "BiquaQuad"; break;
    case VTK_QUADRATIC_TETRA:
        stype = "QuaTetra"; break;
    case VTK_QUADRATIC_PYRAMID:
        stype = "QuaPyramid"; break;
    case VTK_QUADRATIC_HEXAHEDRON:
        stype = "QuaHexa"; break;
    case VTK_BIQUADRATIC_QUADRATIC_HEXAHEDRON:
        stype = "BiquaQuaHexa"; break;
    case VTK_TRIQUADRATIC_HEXAHEDRON:
        stype = "TriquaHexa"; break;
    case VTK_QUADRATIC_LINEAR_WEDGE:
        stype = "QuaLinearWedge"; break;
    case VTK_QUADRATIC_WEDGE:
        stype = "QuaWedge"; break;
    case VTK_BIQUADRATIC_QUADRATIC_WEDGE:
        stype = "BiquaQuaWedge"; break;
    default:
        break;
    }

    return stype;
}

QList<VTKCellType> MeshChecker::getInputCellType()
{
    return _inputCount.keys();
}

int MeshChecker::getInputCellCount(VTKCellType type)
{
    int count = 0;
    if (_inputCount.contains(type))
        count = _inputCount.value(type);
    return count;
}

int MeshChecker::getInputCellCount()
{
    int count = 0;
    QList<VTKCellType> typelist = this->getInputCellType();
    const int n = typelist.size();
    for (int i = 0; i < n; ++i)
    {
        VTKCellType t = typelist.at(i);
        count += this->getInputCellCount(t);
    }
    return count;
}

QList<VTKCellType> MeshChecker::getQualityCellType()
{
    return _checkedCount.keys();
}

int MeshChecker::gettQualityCellCount(VTKCellType type)
{
    int count = 0;
    if (_checkedCount.contains(type))
        count = _checkedCount.value(type).size();
    return count;
}

int MeshChecker::gettQualityCellCount()
{
    int count = 0;
    QList<VTKCellType> typelist = this->getQualityCellType();
    const int n = typelist.size();
    for (int i = 0; i < n; ++i)
    {
        VTKCellType t = typelist.at(i);
        count += this->gettQualityCellCount(t);
    }
    return count;
}

void MeshChecker::getQualityRange(double* range)
{
    _qualityArray->GetRange(range);
    // 		range[0] = r[0];
    // 		range[1] = r[1];
}

double MeshChecker::getAverageQuality()
{
    const int n = _qualityArray->GetNumberOfValues();
    if (n == 0) return -1e66;
    double sum = 0;
    for (int i = 0; i < n; ++i)
    {
        sum += _qualityArray->GetValue(i);
    }
    return sum / n;
}

void MeshChecker::countResult()
{
    _ticks.clear();
    _cellCount.clear();
    const int count = gettQualityCellCount();
    if (count == 0) return;

    double range[2];
    this->getQualityRange(range);

    if (range[1] - range[0] < 1e-8)
    {
        _ticks.append((range[0] + range[1]) / 2);
        QList<VTKCellType> types = this->getQualityCellType();
        for (int i = 0; i < types.size(); ++i)
        {
            VTKCellType t = types.at(i);
            int n = this->gettQualityCellCount(t);
            _cellCount[t].append(n);
        }
        return;
    }

    QList<VTKCellType> types = this->getQualityCellType();
    for (int i = 0; i < types.size(); ++i)
    {
        VTKCellType t = types.at(i);
        //int n = this->gettQualityCellCount(t);
        _cellCount[t].resize(10);
    }
    double ran = range[1] - range[0];
    double step = ran / 10.0;
    for (int i = 0; i < 10; ++i)
        _ticks.append(range[0] + step / 2 + step*i);

    for (int i = 0; i < types.size(); ++i)
    {
        VTKCellType t = types.at(i);
        QList<double> qual = _checkedCount[t];
        int cellcount = qual.size();
        for (int c = 0; c < cellcount; ++c)
        {
            double v = qual.at(c);
            for (int j = 0; j < 10; ++j)
            {
                double ti = _ticks.at(j);
                if (v >= ti - step / 2 && v < ti + step / 2)
                {
                    ++_cellCount[t][j];
                    continue;
                }
            }
        }

    }
}

QVector<double> MeshChecker::getTicks()
{
    return _ticks;
}

QVector<double> MeshChecker::getCountList(VTKCellType t)
{
    QVector<double> d;
    if (_cellCount.contains(t))
        d = _cellCount.value(t);
    return d;
}

QList<vtkDataSet*> MeshChecker::getResultList()
{
    return _resultList;
}



}
