//- ****************************************************************************
//-
//- Copyright 2009 National Technology & Engineering Solutions of Sandia, LLC
//- (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
//- Government retains certain rights in this software.
//-
//- BSD Open Source License
//- All rights reserved.
//-
//- Redistribution and use in source and binary forms, with or without
//- modification, are permitted provided that the following conditions are met:
//-
//-   1. Redistributions of source code must retain the above copyright notice,
//-      this list of conditions and the following disclaimer.
//-
//-   2. Redistributions in binary form must reproduce the above copyright
//-      notice, this list of conditions and the following disclaimer in the
//-      documentation and/or other materials provided with the distribution.
//-
//-   3. Neither the name of the copyright holder nor the names of its
//-      contributors may be used to endorse or promote products derived from
//-      this software without specific prior written permission.
//-
//- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
//- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//- POSSIBILITY OF SUCH DAMAGE.
//-
//- ****************************************************************************

#include "Grid.h"
#include "GeoStack.h"
#include "InterpolatedProfile.h"
#include "SLBMException.h"

#include <string>
#include <iostream>

//using namespace std;

// **** _BEGIN SLBM NAMESPACE_ **************************************************

namespace slbm {

// **** _STATIC INITIALIZATIONS_************************************************

int GeoStack::geoStackClassCount = 0;

GeoStack::GeoStack()
{
    ++geoStackClassCount;
}

GeoStack::GeoStack(const int& i, double* depths, double* pvelocities, 
                   double* svelocities, double* gradients) 
                   : index(i), refCount(0)
{
    ++geoStackClassCount;

    for (int i=0; i<NLAYERS; i++)
    {
        depth[i] = depths[i];
        pvelocity[i] = pvelocities[i];
        svelocity[i] = svelocities[i];
    }
    gradient[PWAVE] = gradients[PWAVE];
    gradient[SWAVE] = gradients[SWAVE];

    thicknessTest();
}

GeoStack::GeoStack(const GeoStack &other) : index(-1), refCount(0)
{
    ++geoStackClassCount;

    for (int i=0; i<NLAYERS; i++)
    {
        depth[i] = other.depth[i];
        pvelocity[i] = other.pvelocity[i];
        svelocity[i] = other.svelocity[i];
    }
    gradient[PWAVE] = other.gradient[PWAVE];
    gradient[SWAVE] = other.gradient[SWAVE];
}

GeoStack::~GeoStack()
{
    --geoStackClassCount;
}

GeoStack& GeoStack::operator=(const GeoStack& other)
{
    for (int i=0; i<NLAYERS; i++)
    {
        depth[i] = other.depth[i];
        pvelocity[i] = other.pvelocity[i];
        svelocity[i] = other.svelocity[i];
    }
    gradient[PWAVE] = other.gradient[PWAVE];
    gradient[SWAVE] = other.gradient[SWAVE];
    return *this;
}

bool GeoStack::operator==(const GeoStack& other)
{
    for (int i=0; i<NLAYERS; i++)
    {
        if (depth[i] != other.depth[i]) return false;
        if (pvelocity[i] != other.pvelocity[i]) return false;
        if (svelocity[i] != other.svelocity[i]) return false;
    }
    if (gradient[PWAVE] != other.gradient[PWAVE]) return false;
    if (gradient[SWAVE] != other.gradient[SWAVE]) return false;
    return true;
}

bool GeoStack::hasLowVelocityZone()
{
    double vp = 0, vs = 0;
    for (int i=SEDIMENT1; i<=UPPER_CRUST; ++i)
        if (depth[i+1] - depth[i] > 1e-6)
        {
            vp = max(vp, pvelocity[i]);
            vs = max(vs, svelocity[i]);
        }

    if (vp > pvelocity[MIDDLE_CRUST_G]) return true;

    //if (vs > svelocity[MIDDLE_CRUST_G]) return true;

    for (int i=MIDDLE_CRUST_N; i<=LOWER_CRUST; ++i)
        if (depth[i+1] - depth[i] > 1e-6)
        {
            vp = max(vp, pvelocity[i]);
            vs = max(vs, svelocity[i]);
        }

    if (vp > pvelocity[MANTLE]) return true;

    if (vs > svelocity[MANTLE]) return true;

    return false;
}

string GeoStack::toString()
{
    ostringstream os;

    os << setiosflags(ios::fixed) << setiosflags(ios::showpoint) << setprecision(4);

    string layers[NLAYERS] = {"water", "sediment1", "sediment2", "sediment3",
            "upper_crust", "middle_crust_n", "middle_crust_g", "lower_crust", "mantle"};

    int w = 9;

    os << " " << setw(5) << "#" 
        << left 
        << " " << setw(15) << "Layer" 
        << right
        << setw(w-1) << "P_Vel" 
        << setw(w-1) << "S_Vel" 
        << setw(w) << "Top" 
        << setw(w) << "Thick" 
        << endl;

    --w;
    for (int i=0; i<NLAYERS; i++)
    {
        // layer number
        os << " " << setw(5) << i+1;

        // layer name
        os << " " << setw(15) << left << layers[i] << right;

        // velocity
        os << " " << setw(w-1) << pvelocity[i];

        // velocity
        os << " " << setw(w-1) << svelocity[i];

        // depth
        os << " " << setw(w) << depth[i];

        // thickness
        if (i < NLAYERS-1)
        {
        if (i == MIDDLE_CRUST_N)
            os << " " << setw(w) << depth[LOWER_CRUST]-depth[MIDDLE_CRUST_N];
        else 
            os << " " << setw(w) << depth[i+1]-depth[i];
        }

        os << endl;
    }

    return os.str();
}

int GeoStack::getClassCount()
{
  return geoStackClassCount;
}

bool GeoStack::thicknessTest()
{
    bool modified = false;
    for (int i=1; i<NLAYERS; i++)
        if (depth[i-1] > depth[i])
        {
            if (depth[i-1] - depth[i] < 0.002)
            {
                depth[i] = depth[i-1];
                modified = true;
            }
            else
            {
                ostringstream os;
                os << endl << "ERROR in GridSLBM::saveVelocityModel()" << endl
                        << "Layer " << i << " has negative thickness" << endl
                        << "depths[" << i-1 << "] = " << setw(11) << depth[i-1] << endl
                        << "depths[" << i   << "] = " << setw(11) << depth[i] << endl
                        << "Version " << SlbmVersion << "  File " << __FILE__
                        << " line " << __LINE__ << endl    << endl;

                throw SLBMException(os.str(), 999);
            }

        }
    return modified;
}

} // end slbm namespace
