package com.partlyhuman.apollo.logging
{
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.system.Capabilities;
    
    import mx.core.mx_internal;
    import mx.logging.targets.LineFormattedTarget;
    
    use namespace mx_internal;

    /**
     * Logging target that logs to a file of your choice in the (Apollo) application's
     * resource directory. You may choose the filename and whether the file will append onto
     * an existing log (be careful of this one as I don't really cut the log down when it reaches
     * some size limit, though this would be simple enough to add) or clear the log per session.
     * 
     * Accepts all options of TraceTarget, so see the documentation there. You may set filters
     * for the severity of events that are logged, how much information is logged with the message,
     * the category of events that are logged, etc.
     * 
     * @author Roger Braunstein | partlyhuman.com
     * @see mx.logging.targets.TraceTarget
     */
    public class FileTarget extends LineFormattedTarget
    {
        [Inspectable(category="General", defaultValue="log.txt")]
        public var filename:String = "log.txt";
        
        [Inspectable(category="General", defaultValue="false")]
        public var append:Boolean = false;
        
        protected var _outstream:FileStream;
        protected var failedToOpen:Boolean = false;
        protected var NEWLINE:String = "\n";
        
        public function FileTarget()
        {
            super();
            determineNewline();
        }
        
        /**
         * Determines what newline character to use. Provided for your overriding.
         */
        protected function determineNewline():void
        {
            if (Capabilities.os.match(/Windows/)) NEWLINE = "\r\n";
        }
        
        /**
         * Makes sure the filename is something you really want to write to. You might
         * be careful of relative filenames. But, this value is set in the application
         * itself, so I'm trusting myself to not be silly about it.
         * Override this to be less trusting.
         */
        protected function getSanitizedFilename():String
        {
            return filename;
        }
        
        /**
         * Lazily opens the stream. This lets you set the filename outside the constructor
         * or change it before the first logging event takes place.
         */
        protected function get outstream():FileStream
        {
            if (_outstream || failedToOpen) return _outstream;
            
            try
            {
                var fileref:File = File.appStorageDirectory.resolve(getSanitizedFilename());
                _outstream = new FileStream();
                _outstream.open(fileref, (append)?FileMode.APPEND:FileMode.WRITE);    
            } catch (e:Error) {
                _outstream = null;
                failedToOpen = true;
                trace("ERROR: FileTarget wasn't able to open log file as specified, nothing will be logged.");
            }
            return _outstream;
        }
        
        /**
         * Using the LineFormattedTarget class to do the "heavy" lifting.
         */
        override mx_internal function internalLog(message:String):void
        {
            outstream && outstream.writeUTFBytes(message + NEWLINE);
        }
        
    }
}