//------------------------------------------------------------------------------
// <copyright file="ConnectionManagerRUDICS.cpp" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
// 	Implementation of ConnectionManagerRUDICS class, which
//  manages a channel over RUDICS.
// </summary>
//
// <owner>Arash Ushani, using old code from SCPExecutor.cpp</owner>
//------------------------------------------------------------------------------

#include "ConnectionManagerRUDICS.h"
#include "LqrSMACSharedLib.h"
#include <fstream>
#include <boost/algorithm/string.hpp>

using namespace std;
using namespace boost;
using namespace LiquidRobotics::PPPHostLib;
using namespace LiquidRobotics::SMACFramework;

namespace LiquidRobotics {
namespace ConnectionManager {

ConnectionManagerRUDICS::ConnectionManagerRUDICS(const ConnectionManagerChannelSettings& _settings, Logger::Ptr _logger)
: ConnectionManagerChannel(_settings, _logger)
, queue(_settings.GetQueueCapacity())
{
}

ConnectionManagerRUDICS::ConnectionManagerRUDICS(const ConnectionManagerRUDICS& rhs)
: ConnectionManagerChannel(rhs)
{
}

bool ConnectionManagerRUDICS::ChannelOn()
{
	bool success = false;

	Logger::Locked(*logger)->Info << "RUDICS starting to connect" << endl;

	PPPHost::Ptr pppHost2 = pppHost;

	if (!pppHost2)
	{
		pppHost.reset(new PPPHost(settings.GetPPPSettings(), logger));

		try
		{
			pppConnection = pppHost->Subscribe(boost::bind(&ConnectionManagerRUDICS::PPPHostCallback, this, _1, _2));
		}
		catch(...)
		{
			pppHost.reset();
			throw;
		}

		pppHost2 = pppHost;
	}

	if (pppHost2 && (pppHost2->GetConnectionStatus() != PPPHost::CONNECTED))
	{
		pppHost2->Connect();

		bool looping = true;

		while(looping)
		{
			QueueEntry item = queue.Read();

			switch(item.type)
			{
			case QueueEntry::CONNECTION_STATE_CHANGE:

				if (item.newStatus == PPPHost::CONNECTED)
				{
					string serialNumberFileName = settings.GetModemSerialNumberFilename();
					boost::trim(serialNumberFileName);

					if (serialNumberFileName.length())
					{
						string modemSerialNumber = pppHost2->GetModemSerialNumber();

						{
							Logger::Lock logLock(*logger);
							logger->Info << "Saving modem serial number '" << modemSerialNumber << "' to " << serialNumberFileName << endl;
						}

						std::ofstream ofs(serialNumberFileName.c_str());

						// if the write stream is no good then throw invalid argument exception
						if (!ofs.good())
						{
							Logger::Lock logLock(*logger);
							logger->Error << "Could not open " << serialNumberFileName << " for write" << endl;
						}
						else
							try
							{
								ofs << modemSerialNumber;
							}
							catch(std::exception& ex)
							{
								Logger::Lock logLock(*logger);
								logger->Error << "Caught exception " << typeid(ex).name() << " '" << ex.what() << "' while saving modem serial number to file" << endl;
							}
					}

					string simCardFileName = settings.GetModemSimCardFilename();
					boost::trim(simCardFileName);

					if (simCardFileName.length())
					{
						string simCardNumber = pppHost2->GetModemSimCardNumber();

						{
							Logger::Lock logLock(*logger);
							logger->Info << "Saving modem SIM card number '" << simCardNumber << "' to " << simCardFileName << endl;
						}

						std::ofstream ofs(simCardFileName.c_str());

						// if the write stream is no good then throw invalid argument exception
						if (!ofs.good())
						{
							Logger::Lock logLock(*logger);
							logger->Error << "Could not open " << simCardFileName << " for write" << endl;
						}
						else
							try
							{
								ofs << simCardNumber;
							}
							catch(std::exception& ex)
							{
								Logger::Lock logLock(*logger);
								logger->Error << "Caught exception " << typeid(ex).name() << " '" << ex.what() << "' while saving SIM card number to file" << endl;
							}
					}

					looping = false;
					success = true;
				}

				break;

			case QueueEntry::SHUT_DOWN:
				{
					Logger::Lock logLock(*logger);
					logger->Info << "Shut down detected while awaiting connect" << endl;
					success = false;
					break;
				}

			default:
				{
					Logger::Lock logLock(*logger);
					logger->Info << "Unhandled message type " << item.type << " while awaiting connect (ignored)" << endl;
				}
				break;
			}
		}
	}

	logger->Info << "Connection " << (success ? "was SUCCESSFUL!":"FAILED!") << endl;
	return success;
}

bool ConnectionManagerRUDICS::ChannelOff()
{
	Logger::Lock logLock(*logger);
	logger->Info << "RUDICS starting to disconnect" << endl;

	PPPHost::Ptr pppHost2 = pppHost;

	if (pppHost2 && (pppHost2->GetConnectionStatus() != PPPHost::DISCONNECTED))
	{
		pppHost2->Disconnect();

		bool looping = true;

		while(looping)
		{
			QueueEntry item = queue.Read();

			switch(item.type)
			{
			case QueueEntry::CONNECTION_STATE_CHANGE:

				if (item.newStatus == PPPHost::DISCONNECTED)
					looping = false;

				break;

			case QueueEntry::SHUT_DOWN:

				//throw ShutdownRequested("Shut down detected while awaiting disconnect");
				return true;

			default:

				Logger::Lock logLock(*logger);
				logger->Info << "Unhandled message type " << item.type << " while awaiting disconnect (ignored)" << endl;

				break;
			}
		}
	}

	pppConnection.reset();
	pppHost.reset();

	return true;
}

void ConnectionManagerRUDICS::PPPHostCallback(PPPHost& sender, ConnectionStatus connectionStatus)
{
	logger->Info << "PPPHost Call back" << endl;
	queue.Write(QueueEntry(QueueEntry::CONNECTION_STATE_CHANGE, connectionStatus));
}

}
}
