/*------------------------------------------------------------------------
* (The MIT License)
* 
* Copyright (c) 2008-2011 Rhomobile, 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.
* 
* http://rhomobile.com
*------------------------------------------------------------------------*/

using System;
using rho.logging;
using IronRuby.Runtime;
using IronRuby.Builtins;

namespace rho.common
{
    public class RhoLogger
    {
        public static boolean RHO_STRIP_LOG = false;

        private static int L_TRACE = 0;
	    private static int L_INFO = 1;
	    private static int L_WARNING = 2;
	    private static int L_ERROR = 3;
	    private static int L_FATAL = 4;
	
	    private String[] LogSeverityNames = { "TRACE", "INFO", "WARNING", "ERROR", "FATAL" };
	
	    private String m_category;
	    private static RhoLogConf m_oLogConf = new RhoLogConf();
	    private String m_strMessage;
	    private int    m_severity;
	    private static Mutex m_SinkLock = new Mutex();
        private static Mutex m_SinkLock2 = new Mutex();
	    //private static IRhoRubyHelper m_sysInfo;
	    public static String LOGFILENAME = "RhoLog.txt";

        public RhoLogger(String name)
        {
		    m_category = name;
	    }

        public static RhoLogConf getLogConf(){
		    return m_oLogConf;
	    }

        public String getLogCategory() { return m_category; }
        public void setLogCategory(String category) { m_category = category; }

        public static void close() { m_oLogConf.close(); }

        private boolean isEnabled()
        {
            if (m_severity >= getLogConf().getMinSeverity())
            {
                if (m_category.length() == 0 || m_severity >= L_ERROR)
                    return true;

                return getLogConf().isCategoryEnabled(m_category);
            } 

            return false;
        }

        private String get2FixedDigit(int nDigit)
        {
		    if ( nDigit > 9 )
		    	return nDigit.ToString();
			
		    return "0" + nDigit.ToString();
	    }

        private String get3FixedDigit(int nDigit)
        {
            if (nDigit > 99)
                return nDigit.ToString();

            if (nDigit > 9 )
                return "0" + nDigit.ToString();

            return "00" + nDigit.ToString();
        }

	    private String getLocalTimeString()
        {
            DateTime time = DateTime.Now;

		    String strTime = "";
		    strTime += 
			    get2FixedDigit(time.Month) + "/" + 
			    get2FixedDigit(time.Day) + "/" +
			    time.Year + " " + 
			    get2FixedDigit(time.Hour) + ":" + 
			    get2FixedDigit(time.Minute) +	":" + 
			    get2FixedDigit(time.Second);
			
			    //if ( false ) //comment this to show milliseconds
				    strTime += ":" + get3FixedDigit(time.Millisecond);
			
		    return strTime;
	    }
	
	    private String makeStringSize(String str, int nSize)
	    {
		    if ( str.length() >= nSize )
			    return str.substring(0, nSize);
		    else {
			    String res = "";
			    for( int i = 0; i < nSize - str.length(); i++ )
				    res += ' ';
			
			    res += str;
			
			    return res;
		    }
	    }

	    private String makeStringSizeEnd(String str, int nSize)
	    {
		    if ( str.length() >= nSize )
			    return str.substring(str.length()-nSize, str.length());
		    else {
			    String res = "";
			    for( int i = 0; i < nSize - str.length(); i++ )
				    res += ' ';
			
			    res += str;
			
			    return res;
		    }
	    }
	
	    private String getThreadField()
        {
		    String strThread = System.Threading.Thread.CurrentThread.Name;
            if (strThread == null || strThread.length() == 0)
                strThread = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString();

		    return strThread;
	    }
	
	    private void addPrefix()
        {
	        //(log level, local date time, thread_id, file basename, line)
	        //I time f5d4fbb0 category|

	        if ( m_severity == L_FATAL )
	    	    m_strMessage += LogSeverityNames[m_severity];
	        else
	    	    m_strMessage += LogSeverityNames[m_severity].charAt(0);

	        m_strMessage += " " + getLocalTimeString() + ' ' + makeStringSizeEnd(getThreadField(),9) + ' ' +
	    	    makeStringSize(m_category,15) + "| ";
	    }

        private void logMessage( int severity, String msg ){
	        logMessage(severity, msg, null, false );
        }
        private void logMessage( int severity, String msg, Exception e ){
	        logMessage(severity, msg, e, false );
        }

        protected virtual void logMessage(int severity, String msg, Exception e, boolean bOutputOnly)
        {
            m_severity = severity;
		    if ( !isEnabled() )
			    return;
		
		    m_strMessage = "";
	        if ( getLogConf().isLogPrefix() )
	            addPrefix();
		
	        if ( msg != null )
	    	    m_strMessage += msg;
	    
	        if ( e != null )
	        {
	    	    m_strMessage += (msg != null && msg.length() > 0 ? ";" : "") + e.GetType().FullName + ": ";
	    	
	    	    String emsg = e.Message;
	    	    if ( emsg != null )
	    		    m_strMessage += emsg;

                String trace = e.StackTrace;
                if (trace != null)
                    m_strMessage += ";TRACE: \n" + trace;
	        }
	    
		    if (m_strMessage.length() > 0 || m_strMessage.charAt(m_strMessage.length() - 1) != '\n')
			    m_strMessage += '\n';
			
		    if ( bOutputOnly )
		    {
                getLogConf().getOutputSink().writeLogMessage(m_strMessage);
		    }else
		    {
		        lock( m_SinkLock ){
		    	    getLogConf().sinkLogMessage( m_strMessage, bOutputOnly );
		        }
		    }
	        if ( m_severity == L_FATAL )
	    	    processFatalError();
        }

        static boolean isSimulator()
        {
            return Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Emulator;
        }

        protected void processFatalError()
        {
            if (isSimulator())
                throw new Exception("Fatal error.");

            System.Threading.Thread.CurrentThread.Abort();
        }

        public void TRACE(String message)
        {
            logMessage(L_TRACE, message);
        }
        public void TRACE(String message, Exception e)
        {
            logMessage(L_TRACE, message, e);
        }

        public void INFO(String message)
        {
            logMessage(L_INFO, message);
        }

        public void INFO_OUT(String message)
        {
            logMessage(L_INFO, message, null, true);
        }

        public void INFO_EVENT(String message)
        {
            //EventLogger.logEvent(EVENT_GUID, (m_category + ": " + message).getBytes());

            INFO_OUT(message);
        }

        public void WARNING(String message)
        {
            logMessage(L_WARNING, message);
        }
        public void ERROR(String message)
        {
            logMessage(L_ERROR, message);
        }
        public void ERROR(Exception e)
        {
            logMessage(L_ERROR, "", e);
        }
        public void ERROR(String message, Exception e)
        {
            logMessage(L_ERROR, message, e);
        }
        public void ERROR_OUT(String message, Exception e) 
        {
		    logMessage( L_ERROR, message, e, true );
	    }

        public void ERROR_EVENT(String message, Exception e)
        {
            ERROR_OUT(message, e);

            //EventLogger.logEvent(EVENT_GUID, m_strMessage.getBytes());
        }

        public virtual void FATAL(String message)
        {
            logMessage(L_FATAL, message);
        }
        public virtual void FATAL(Exception e)
        {
            logMessage(L_FATAL, "", e);
        }
        public virtual void FATAL(String message, Exception e)
        {
            logMessage(L_FATAL, message, e);
        }

        public void ASSERT(boolean exp, String message)
        {
            if (!exp)
                logMessage(L_FATAL, message);
        }

        public static String getLogText(){
		    return m_oLogConf.getLogText();
	    }
	
	    public static int getLogTextPos(){
		    return m_oLogConf.getLogTextPos();
	    }
	
	    public static void clearLog(){
	        lock( m_SinkLock ){
	    	    getLogConf().clearLog();
	        }
	    }

        public void HandleRubyException(Exception ex, Exception rubyEx,  String message)
        {
            if (rubyEx == null)
            {
                rubyEx = RubyExceptionData.InitializeException(new RuntimeError(ex.Message.ToString()), ex.Message);
            }
            logMessage(L_ERROR, message);
            throw rubyEx;
        }
	
        public static void InitRhoLog()
        {
            RhoConf.InitRhoConf();
        
            //Set defaults
    	    m_oLogConf.setLogPrefix(true);		
    	
    	    m_oLogConf.setLogToFile(true);

            //TODO - if ip is empy in rhoconfig then we have to set to false
        
		    if ( isSimulator() ) {
			    m_oLogConf.setMinSeverity( L_INFO );
			    m_oLogConf.setLogToOutput(true);
			    m_oLogConf.setEnabledCategories("*");
			    m_oLogConf.setDisabledCategories("");
	    	    m_oLogConf.setMaxLogFileSize(0);//No limit
		    }else{
			    m_oLogConf.setMinSeverity( L_ERROR );
			    m_oLogConf.setLogToOutput(false);
			    m_oLogConf.setEnabledCategories("");
	    	    m_oLogConf.setMaxLogFileSize(1024*50);
		    }
		
    	    if ( RhoConf.getInstance().getRhoRootPath().length() > 0 )
	    	    m_oLogConf.setLogFilePath( RhoConf.getInstance().getRhoRootPath() + LOGFILENAME );

            //load configuration if exist
    	    //
    	    //m_oLogConf.saveToFile("");
    	    //
    	    RhoConf.getInstance().loadConf();
    	    m_oLogConf.loadFromConf(RhoConf.getInstance());

            if (RhoConf.getInstance().getString("rhologhost").length() > 0)
                m_oLogConf.setServerSynk(new rho.logging.RhoLogServerSink(m_oLogConf));
        }
    }

}