//////////////////////////////////////////////////////////////////////////////
//
// Phasor.h, (c) 2006-2017, Ryan A. Colyer
//
// For detail on the theory and mathematics behind the formulas used here,
// you can consult the following two references.  These would also be
// appropriate references to cite if reusing this library in an academic
// publication, as this was initially developed in concert with those two
// publications.
// 
// A Novel Fluorescence Lifetime Imaging System that Optimizes Photon
// Efficiency - Ryan A. Colyer, Claudia Lee, Enrico Gratton
// Microsc Res Tech 71(3):201-13, 2008
//
// Phasor imaging with a widefield photon-counting detector -
// Ryan A. Colyer, Oswald H. W. Siegmund, Anton S. Tremsin, John V. Vallerga,
// Shimon Weiss, Xavier Michalet
// Journal of Biomedical Optics 17(1), 016008, 2012  
//
//////////////////////////////////////////////////////////////////////////////
//
// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef PHASOR_H
#define PHASOR_H

#include "macros.h"
#include "RC/RC.h"
#include <math.h>

using namespace std;


namespace PhasorApp {
  class Phasor {
    public:

    // Construct a phasor with 0 counts.
    Phasor();

    // Clear the phasor of counts.
    void Clear();
    // Get the modulation value of the phasor.
		float GetM() const;
    // Get the phase (angle) of the phasor.
		float GetPhi() const;
    // Set the phasor from normalized g and s values.
    void SetGS(float g, float s);
    // Set the phasor from modulation and phase values.
		void SetMPhi(float m, float phi);
    // Set the phasor from a lifetime and frequency.
		void SetTau(float tau, float freq);
    // Get the phase lifetime of this phasor for a given frequency.
		float GetTauP(float freq) const;
    // Get the modulation lifetime of this phasor for a given frequency.
		float GetTauM(float freq) const;
    // Perform a reference calculation for a modulation factor and phase shift.
		void Reference(float m_fact, float phi_shift);
    // Perform a reference calculation including a background adjustment.
		void ReferenceBG(float m_fact, float phi_shift, float background);
    // Return the modulation factor to convert this sample to the target value.
    float IsReferenceMod(Phasor target) const;
    // Return the phase shift to convert this sample to the target value.
    float IsReferencePhi(Phasor target) const;
    // Return the modulation factor including a background adjustment.
    float IsReferenceModBG(Phasor target, float background) const;
    // Reference only the phase value.
		void PhaseReference(float phi_shift);

    // Add a photon count to this phasor at the given phase.
    inline void AddCount(float phase) {
      counts++;
      bigG += cos(phase);
      bigS += sin(phase);

      bigG2 += cos(2*phase);
      bigS2 += sin(2*phase);
    }

    // Return the modulation value of the second harmonic.
    float GetM2() const;
    // Return the phase value of the second harmonic.
    float GetPhi2() const;
    // Return the theoretical standard deviation in phi.
    // Only valid for an unreferenced phasor.
    float SigmaPhi() const;

    // Return the theoretical standard deviation in modulation.
    // Only valud for an unreferenced phasor.
    // The input m_fact = 1/m_IR from the paper.
    float SigmaM(float m_fact) const;

    // Return the theoretical standard deviation in the phase lifetime.
    // The input phi_shift = -phi_IR from the paper.
    float SigmaTauP(float freq_times_harmonic, float phi_shift) const;

    // Return the theoretical standard deviation in the modulation lifetime.
    // The input m_fact = 1/m_IR from the paper.
    float SigmaTauM(float freq_times_harmonic, float m_fact) const;

    // Add a phasor to this one.
    inline Phasor& operator+=(const Phasor &ph) {
      counts += ph.counts;
      bigG += ph.bigG;
      bigS += ph.bigS;
      bigG2 += ph.bigG2;
      bigS2 += ph.bigS2;

      return *this;
    }

    // Subtract a phasor from this one.
    inline Phasor& operator-=(const Phasor &ph) {
      counts -= ph.counts;
      bigG -= ph.bigG;
      bigS -= ph.bigS;
      bigG2 -= ph.bigG2;
      bigS2 -= ph.bigS2;

      return *this;
    }

    // Scale this phasor by a constant factor.
    inline Phasor& operator*=(const float fact) {
      counts *= fact;
      bigG *= fact;
      bigS *= fact;
      bigG2 *= fact;
      bigS2 *= fact;

      return *this;
    }

    // Scale this phasor down by a constant factor.
    inline Phasor& operator/=(const float fact) {
      counts /= fact;
      bigG /= fact;
      bigS /= fact;
      bigG2 /= fact;
      bigS2 /= fact;

      return *this;
    }


    // Add two phasors.
    inline const Phasor operator+(const Phasor &ph) const {
      Phasor result = *this;
      result += ph;
      return result;
    }

    // Subtract two phasors.
    inline const Phasor operator-(const Phasor &ph) const {
      Phasor result = *this;
      result -= ph;
      return result;
    }

    // Scale a phasor by a constant factor.
    inline const Phasor operator*(const float fact) const {
      Phasor result = *this;
      result *= fact;
      return result;
    }

    // Scale a phasor down by a constant factor.
    inline const Phasor operator/(const float fact) const {
      Phasor result = *this;
      result /= fact;
      return result;
    }


    // Return a string representation of the phasor's two harmonics.
    inline RC::RStr ToString() const {
      return RC::RStr(counts) + " (" + g() + ", " + s() + ")"
             + " (" + g2() + ", " + s2() + ")";
    }


    // Return the normalized g value.
    inline float g() const { return (bigG / counts); }
    // Return the normalized s value.
    inline float s() const { return (bigS / counts); }

    // Return the normalized second harmonic g value.
    inline float g2() const { return (bigG2 / counts); }
    // Return the normalized second harmonic s value.
    inline float s2() const { return (bigS2 / counts); }


    float counts;  // The number of photons the phasor represents.
    float bigG;    // The unnormalized big-G value.
    float bigS;    // The unnormalized big-S value.

    float bigG2;   // The unnormalized big-G value for the second harmonic.
    float bigS2;   // The unnormalized big-S value for the second harmonic.
  };

  // Outputs a phasor's value as text "(g, s) (g2, s2)"
  inline ostream& operator<< (ostream &out, const Phasor &ph) {
    out << ph.ToString();
    return out;
  }

  // Scale a phasor by multiplication with a constant value on the left.
  inline const Phasor operator* (const float& left, const Phasor& right) {
    return right * left;
  }
}

#endif // PHASOR_H

