#ifndef GlobalErrorExtendedType_H
#define GlobalErrorExtendedType_H

#include "DataFormats/GeometryCommonDetAlgo/interface/DeepCopyPointer.h"
#include "DataFormats/Math/interface/AlgebraicROOTObjects.h"
#include "DataFormats/GeometryVector/interface/GlobalPoint.h"
//
// Exceptions
//
#include "FWCore/Utilities/interface/Exception.h"

template <class T, class ErrorWeightType>
class GlobalErrorBaseExtended {
public:
  /// Tag to request a null error matrix
  class NullMatrix {};

  GlobalErrorBaseExtended() {}
  GlobalErrorBaseExtended(const NullMatrix&) {}

  /**
   * Constructor.
   * The symmetric matrix stored as a lower triangular matrix
   */
  GlobalErrorBaseExtended(T c11,
                          T c21,
                          T c31,
                          T c41,
                          T c51,
                          T c61,
                          T c22,
                          T c32,
                          T c42,
                          T c52,
                          T c62,
                          T c33,
                          T c43,
                          T c53,
                          T c63,
                          T c44,
                          T c54,
                          T c64,
                          T c55,
                          T c65,
                          T c66) {
    theCartesianError(0, 0) = c11;
    theCartesianError(1, 0) = c21;
    theCartesianError(2, 0) = c31;
    theCartesianError(3, 0) = c41;
    theCartesianError(4, 0) = c51;
    theCartesianError(5, 0) = c61;

    theCartesianError(1, 1) = c22;
    theCartesianError(2, 1) = c32;
    theCartesianError(3, 1) = c42;
    theCartesianError(4, 1) = c52;
    theCartesianError(5, 1) = c62;

    theCartesianError(2, 2) = c33;
    theCartesianError(3, 2) = c43;
    theCartesianError(4, 2) = c53;
    theCartesianError(5, 2) = c63;

    theCartesianError(3, 3) = c44;
    theCartesianError(4, 3) = c54;
    theCartesianError(5, 3) = c64;

    theCartesianError(4, 4) = c55;
    theCartesianError(5, 4) = c65;

    theCartesianError(5, 5) = c66;
  }

  GlobalErrorBaseExtended(const AlgebraicSymMatrix66& err) : theCartesianError(err) {}

  GlobalErrorBaseExtended(const AlgebraicSymMatrix33& err) {
    theCartesianError(0, 0) = err[0][0];
    theCartesianError(1, 0) = err[1][0];
    theCartesianError(2, 0) = err[2][0];
    theCartesianError(3, 0) = 0;
    theCartesianError(4, 0) = 0;
    theCartesianError(5, 0) = 0;

    theCartesianError(1, 1) = err[1][1];
    theCartesianError(2, 1) = err[2][1];
    theCartesianError(3, 1) = 0;
    theCartesianError(4, 1) = 0;
    theCartesianError(5, 1) = 0;

    theCartesianError(2, 2) = err[2][2];
    theCartesianError(3, 2) = 0;
    theCartesianError(4, 2) = 0;
    theCartesianError(5, 2) = 0;

    theCartesianError(3, 3) = 0;
    theCartesianError(4, 3) = 0;
    theCartesianError(5, 3) = 0;

    theCartesianError(4, 4) = 0;
    theCartesianError(5, 4) = 0;

    theCartesianError(5, 5) = 0;
  }

  ~GlobalErrorBaseExtended() {}

  T cxx() const { return theCartesianError(0, 0); }

  T cyx() const { return theCartesianError(1, 0); }

  T czx() const { return theCartesianError(2, 0); }

  T cphixx() const { return theCartesianError(3, 0); }

  T cphiyx() const { return theCartesianError(4, 0); }

  T cphizx() const { return theCartesianError(5, 0); }

  T cyy() const { return theCartesianError(1, 1); }

  T czy() const { return theCartesianError(2, 1); }

  T cphixy() const { return theCartesianError(3, 1); }

  T cphiyy() const { return theCartesianError(4, 1); }

  T cphizy() const { return theCartesianError(5, 1); }

  T czz() const { return theCartesianError(2, 2); }

  T cphixz() const { return theCartesianError(3, 2); }

  T cphiyz() const { return theCartesianError(4, 2); }

  T cphizz() const { return theCartesianError(5, 2); }

  T cphixphix() const { return theCartesianError(3, 3); }

  T cphiyphix() const { return theCartesianError(4, 3); }

  T cphizphix() const { return theCartesianError(5, 3); }

  T cphiyphiy() const { return theCartesianError(4, 4); }

  T cphizphiy() const { return theCartesianError(5, 4); }

  T cphizphiz() const { return theCartesianError(5, 5); }

  /**
   * Access method to the matrix,
   * /return The SymMatrix
   */
  const AlgebraicSymMatrix66& matrix() const { return theCartesianError; }
  const AlgebraicSymMatrix66& matrix_new() const { return theCartesianError; }

  //FIXME to be reimplemented
  T rerr(const GlobalPoint& aPoint) const {
    T r2 = aPoint.perp2();
    T x2 = aPoint.x() * aPoint.x();
    T y2 = aPoint.y() * aPoint.y();
    T xy = aPoint.x() * aPoint.y();
    if (r2 != 0)
      return std::max<T>(0, (1. / r2) * (x2 * cxx() + 2. * xy * cyx() + y2 * cyy()));
    else
      return 0.5 * (cxx() + cyy());
  }

  //FIXME to be reimplemented
  T phierr(const GlobalPoint& aPoint) const {
    T r2 = aPoint.perp2();
    T x2 = aPoint.x() * aPoint.x();
    T y2 = aPoint.y() * aPoint.y();
    T xy = aPoint.x() * aPoint.y();
    if (r2 != 0)
      return std::max<T>(0, (1. / (r2 * r2)) * (y2 * cxx() - 2. * xy * cyx() + x2 * cyy()));
    else
      return 0;
  }

  GlobalErrorBaseExtended operator+(const GlobalErrorBaseExtended& err) const {
    return GlobalErrorBaseExtended(theCartesianError + err.theCartesianError);
  }
  GlobalErrorBaseExtended operator-(const GlobalErrorBaseExtended& err) const {
    return GlobalErrorBaseExtended(theCartesianError - err.theCartesianError);
  }

private:
  AlgebraicSymMatrix66 theCartesianError;
};

#endif
