//**********************************************************************************//
//    Copyright (C) 2009-2015  Ovidio Pena <ovidio@bytesfall.com> // Copyright
//    (C) 2013-2015  Konstantin Ladutenko <kostyfisik@gmail.com>          //
//                                                                                  //
//    This file is part of scattnlay //
//                                                                                  //
//    This program is free software: you can redistribute it and/or modify // it
//    under the terms of the GNU General Public License as published by // the
//    Free Software Foundation, either version 3 of the License, or // (at your
//    option) any later version.                                           //
//                                                                                  //
//    This program is distributed in the hope that it will be useful, // but
//    WITHOUT ANY WARRANTY; without even the implied warranty of //
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the // GNU
//    General Public License for more details. //
//                                                                                  //
//    The only additional remark is that we expect that all publications //
//    describing work using this software, or all commercial products // using
//    it, cite the following reference:                                       //
//    [1] O. Pena and U. Pal, "Scattering of electromagnetic radiation by //
//        a multilayered sphere," Computer Physics Communications, // vol. 180,
//        Nov. 2009, pp. 2348-2354.                                       //
//                                                                                  //
//    You should have received a copy of the GNU General Public License // along
//    with this program.  If not, see <http://www.gnu.org/licenses/>.         //
//**********************************************************************************//
//   This program returns expansion coefficents of Mie series
#include <complex>
#include <cstdio>
#include <string>
#include "../src/nmie-applied-impl.hpp"
#include "../src/nmie-precision.hpp"
#include "./read-spectra.h"

// template<class T> inline T pow2(const T value) {return value*value;}
int main(int argc, char* argv[]) {
  using namespace nmie;
  try {
    read_spectra::ReadSpectra Si_index, Ag_index;
    read_spectra::ReadSpectra plot_core_index_, plot_TiN_;
    std::string core_filename("Si-int.txt");
    // std::string core_filename("Ag.txt");
    // std::string TiN_filename("TiN.txt");
    std::string TiN_filename("Ag-int.txt");
    // std::string TiN_filename("Si.txt");
    std::string shell_filename(core_filename);

    nmie::MultiLayerMieApplied<nmie::FloatType> multi_layer_mie;
    const std::complex<double> epsilon_Si(18.4631066585, 0.6259727805);
    const std::complex<double> epsilon_Ag(-8.5014154589, 0.7585845411);
    const std::complex<double> index_Si = std::sqrt(epsilon_Si);
    const std::complex<double> index_Ag = std::sqrt(epsilon_Ag);
    double WL = 500;             // nm
    double core_width = 5.27;    // nm Si
    double inner_width = 8.22;   // nm Ag
    double outer_width = 67.91;  // nm  Si
    bool isSiAgSi = true;
    double delta_width = 25.0;
    // bool isSiAgSi = false;
    if (isSiAgSi) {
      core_width = 5.27;    // nm Si
      inner_width = 8.22;   // nm Ag
      outer_width = 67.91;  // nm  Si
      multi_layer_mie.AddTargetLayer(core_width, index_Si);
      multi_layer_mie.AddTargetLayer(inner_width, index_Ag);
      multi_layer_mie.AddTargetLayer(outer_width + delta_width, index_Si);
    } else {
      inner_width = 31.93;  // nm Ag
      outer_width = 4.06;   // nm  Si
      multi_layer_mie.AddTargetLayer(inner_width, index_Ag);
      multi_layer_mie.AddTargetLayer(outer_width + delta_width, index_Si);
    }

    for (int dd = 0; dd < 50; ++dd) {
      delta_width = dd;
      FILE* fp;
      std::string fname =
          "absorb-layered-spectra-d" + std::to_string(dd) + ".dat";
      fp = fopen(fname.c_str(), "w");

      multi_layer_mie.SetWavelength(WL);
      multi_layer_mie.RunMieCalculation();

      double Qabs = static_cast<double>(multi_layer_mie.GetQabs());
      printf("Qabs = %g\n", Qabs);
      std::vector<std::vector<std::complex<nmie::FloatType> > > aln, bln, cln,
          dln;
      multi_layer_mie.GetExpanCoeffs(aln, bln, cln, dln);
      std::vector<std::vector<std::complex<double> > > d_aln =
          nmie::ConvertComplexVectorVector<double>(aln);
      std::string str = std::string("#WL ");
      for (int l = 0; l < d_aln.size(); ++l) {
        for (int n = 0; n < 3; ++n) {
          str += "|a|^2+|d|^2_ln" + std::to_string(l) + std::to_string(n) +
                 " " + "|b|^2+|c|^2_ln" + std::to_string(l) +
                 std::to_string(n) + " ";
        }
      }
      str += "\n";
      fprintf(fp, "%s", str.c_str());
      fprintf(fp, "# |a|+|d|");
      str.clear();

      double from_WL = 400;
      double to_WL = 600;
      int total_points = 401;
      double delta_WL = std::abs(to_WL - from_WL) / (total_points - 1.0);
      Si_index.ReadFromFile(core_filename)
          .ResizeToComplex(from_WL, to_WL, total_points)
          .ToIndex();
      Ag_index.ReadFromFile(TiN_filename)
          .ResizeToComplex(from_WL, to_WL, total_points)
          .ToIndex();
      auto Si_data = Si_index.GetIndex();
      auto Ag_data = Ag_index.GetIndex();
      for (int i = 0; i < Si_data.size(); ++i) {
        const double& WL = Si_data[i].first;
        const std::complex<double>& Si = Si_data[i].second;
        const std::complex<double>& Ag = Ag_data[i].second;
        str += std::to_string(WL);
        multi_layer_mie.ClearTarget();
        if (isSiAgSi) {
          multi_layer_mie.AddTargetLayer(core_width, Si);
          multi_layer_mie.AddTargetLayer(inner_width, Ag);
          multi_layer_mie.AddTargetLayer(outer_width + delta_width, Si);
        } else {
          inner_width = 31.93;  // nm Ag
          outer_width = 4.06;   // nm  Si
          multi_layer_mie.AddTargetLayer(inner_width, Ag);
          multi_layer_mie.AddTargetLayer(outer_width + delta_width, Si);
        }
        multi_layer_mie.SetWavelength(WL);
        multi_layer_mie.RunMieCalculation();
        multi_layer_mie.GetQabs();
        multi_layer_mie.GetExpanCoeffs(aln, bln, cln, dln);
        for (int l = 0; l < aln.size(); ++l) {
          for (int n = 0; n < 3; ++n) {
            str += " " +
                   std::to_string(static_cast<double>(
                       pow2(std::abs(aln[l][n])) + pow2(std::abs(dln[l][n])))) +
                   " " +
                   std::to_string(static_cast<double>(
                       pow2(std::abs(bln[l][n])) + pow2(std::abs(cln[l][n]))));

            // str+=" "+std::to_string(aln[l][n].real() -
            // pow2(std::abs(aln[l][n])) 			  +dln[l][n].real() -
            // pow2(std::abs(dln[l][n])))
            //   + " "
            //   + std::to_string(bln[l][n].real() - pow2(std::abs(bln[l][n]))
            // 			  +cln[l][n].real() - pow2(std::abs(cln[l][n]))
            // );
          }
        }
        str += "\n";
        fprintf(fp, "%s", str.c_str());
        str.clear();
      }

      fclose(fp);
    }

    // WL = 500;
    // multi_layer_mie.SetWavelength(WL);
    // multi_layer_mie.RunMieCalculation();
    // multi_layer_mie.GetQabs();
    // multi_layer_mie.GetExpanCoeffs(aln, bln, cln, dln);

    // printf("\n Scattering");
    // for (int l = 0; l<aln.size(); ++l) {
    //   int n = 0;
    //   printf("aln[%i][%i] = %g, %gi\n", l, n+1, aln[l][n].real(),
    //   aln[l][n].imag()); printf("bln[%i][%i] = %g, %gi\n", l, n+1,
    //   bln[l][n].real(), bln[l][n].imag()); printf("cln[%i][%i] = %g, %gi\n",
    //   l, n+1, cln[l][n].real(), cln[l][n].imag()); printf("dln[%i][%i] = %g,
    //   %gi\n", l, n+1, dln[l][n].real(), dln[l][n].imag()); n = 1;
    //   printf("aln[%i][%i] = %g, %gi\n", l, n+1, aln[l][n].real(),
    //   aln[l][n].imag()); printf("bln[%i][%i] = %g, %gi\n", l, n+1,
    //   bln[l][n].real(), bln[l][n].imag()); printf("cln[%i][%i] = %g, %gi\n",
    //   l, n+1, cln[l][n].real(), cln[l][n].imag()); printf("dln[%i][%i] = %g,
    //   %gi\n", l, n+1, dln[l][n].real(), dln[l][n].imag());
    //   // n = 2;
    //   // printf("aln[%i][%i] = %g, %gi\n", l, n+1, aln[l][n].real(),
    //   aln[l][n].imag());
    //   // printf("bln[%i][%i] = %g, %gi\n", l, n+1, bln[l][n].real(),
    //   bln[l][n].imag());
    //   // printf("cln[%i][%i] = %g, %gi\n", l, n+1, cln[l][n].real(),
    //   cln[l][n].imag());
    //   // printf("dln[%i][%i] = %g, %gi\n", l, n+1, dln[l][n].real(),
    //   dln[l][n].imag());
    // }
    // printf("\n Absorbtion\n");
    // for (int l = 0; l<aln.size(); ++l) {
    //   if (l == aln.size()-1) printf(" Total ");
    //   printf("===== l=%i   =====\n", l);
    //   int n = 0;
    //   printf("aln[%i][%i] = %g\n", l, n+1, aln[l][n].real() -
    //   pow2(std::abs(aln[l][n]))); printf("bln[%i][%i] = %g\n", l, n+1,
    //   bln[l][n].real() - pow2(std::abs(bln[l][n]))); printf("cln[%i][%i] =
    //   %g\n", l, n+1, cln[l][n].real() - pow2(std::abs(cln[l][n])));
    //   printf("dln[%i][%i] = %g\n", l, n+1, dln[l][n].real() -
    //   pow2(std::abs(dln[l][n]))); n = 1; printf("aln[%i][%i] = %g\n", l, n+1,
    //   aln[l][n].real() - pow2(std::abs(aln[l][n]))); printf("bln[%i][%i] =
    //   %g\n", l, n+1, bln[l][n].real() - pow2(std::abs(bln[l][n])));
    //   printf("cln[%i][%i] = %g\n", l, n+1, cln[l][n].real() -
    //   pow2(std::abs(cln[l][n]))); printf("dln[%i][%i] = %g\n", l, n+1,
    //   dln[l][n].real() - pow2(std::abs(dln[l][n])));
    //   // n = 2;
    //   // printf("aln[%i][%i] = %g\n", l, n+1, aln[l][n].real() -
    //   pow2(std::abs(aln[l][n])));
    //   // printf("bln[%i][%i] = %g\n", l, n+1, bln[l][n].real() -
    //   pow2(std::abs(bln[l][n])));
    //   // printf("cln[%i][%i] = %g\n", l, n+1, cln[l][n].real() -
    //   pow2(std::abs(cln[l][n])));
    //   // printf("dln[%i][%i] = %g\n", l, n+1, dln[l][n].real() -
    //   pow2(std::abs(dln[l][n])));
    // }

  } catch (const std::invalid_argument& ia) {
    // Will catch if  multi_layer_mie fails or other errors.
    std::cerr << "Invalid argument: " << ia.what() << std::endl;
    return -1;
  }
  return 0;
}
