/***************************************************************************
 *  ExceptionDialog.cs
 *
 *  Copyright (C) 2005-2006 Novell, Inc.
 *  Written by Aaron Bockover <aaron@abock.org>
 ****************************************************************************/

/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
 *
 *  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.
 */
 
using System;
using System.Reflection;
using System.Resources;
using System.Diagnostics;
using System.Collections;
using System.IO;
using Mono.Unix;
using Gtk;

namespace Banshee.Gui.Dialogs
{
    public class ExceptionDialog : Dialog
    {
        private AccelGroup accel_group;
        private string debugInfo;
        
        public ExceptionDialog(Exception e) : base()
        {
            debugInfo = BuildExceptionMessage(e);
           
            HasSeparator = false;
            BorderWidth = 5;
            Resizable = false;
            Title = Catalog.GetString("Banshee Encountered a Fatal Error");
            
            VBox.Spacing = 12;
            ActionArea.Layout = ButtonBoxStyle.End;

            accel_group = new AccelGroup();
		    AddAccelGroup(accel_group);
        
            HBox hbox = new HBox(false, 12);
            hbox.BorderWidth = 5;
            VBox.PackStart(hbox, false, false, 0);
        
            Image image = new Image(Stock.DialogError, IconSize.Dialog);
            image.Yalign = 0.0f;
            hbox.PackStart(image, true, true, 0);

            VBox label_vbox = new VBox(false, 0);
            label_vbox.Spacing = 12;
            hbox.PackStart(label_vbox, false, false, 0);

            Label label = new Label(String.Format("<b><big>{0}</big></b>", GLib.Markup.EscapeText(Title)));
            label.UseMarkup = true;
            label.Justify = Justification.Left;
            label.LineWrap = true;
            label.SetAlignment(0.0f, 0.5f);
            label_vbox.PackStart(label, false, false, 0);

            label = new Label(e.Message);
                
            label.UseMarkup = true;
            label.UseUnderline = false;
            label.Justify = Gtk.Justification.Left;
            label.LineWrap = true;
            label.SetAlignment(0.0f, 0.5f);
            label_vbox.PackStart(label, false, false, 0);

            Label details_label = new Label(String.Format("<b>{0}</b>", 
                GLib.Markup.EscapeText(Catalog.GetString("Error Details"))));
            details_label.UseMarkup = true;
            Expander details_expander = new Expander("Details");
            details_expander.LabelWidget = details_label;
            label_vbox.PackStart(details_expander, true, true, 0);

            ScrolledWindow scroll = new ScrolledWindow();
            TextView view = new TextView();
            
            scroll.HscrollbarPolicy = PolicyType.Automatic;
            scroll.VscrollbarPolicy = PolicyType.Automatic;
            scroll.AddWithViewport(view);
            
            scroll.SetSizeRequest(450, 250);
			
			view.Editable = false;
			view.Buffer.Text = debugInfo;
			
			details_expander.Add(scroll);
			
            hbox.ShowAll();

            IconName = "music-player-banshee";

            AddButton(Stock.Close, ResponseType.Close, true);
        }

        private void AddButton(string stock_id, Gtk.ResponseType response, bool is_default)
        {
            Button button = new Button(stock_id);
            button.CanDefault = true;
            button.Show ();

            AddActionWidget(button, response);

            if(is_default) {
                DefaultResponse = response;
                button.AddAccelerator("activate", accel_group, (uint)Gdk.Key.Return, 
                    0, AccelFlags.Visible);
            }
        }
        
        private string BuildExceptionMessage(Exception e)
        {
            System.Text.StringBuilder msg = new System.Text.StringBuilder();
            
            msg.Append(Catalog.GetString("An unhandled exception was thrown: "));
            msg.Append(e.Message);
            
            msg.Append("\n\n");
            msg.Append(e.StackTrace);
            
            msg.Append("\n");
            
            msg.Append(".NET Version: " + Environment.Version.ToString());
            
            msg.Append("\n\nAssembly Version Information:\n\n");
            
            foreach(Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
				AssemblyName name = asm.GetName();
                msg.Append(name.Name + " (" + name.Version.ToString() + ")\n");
			}
            
            msg.Append("\nPlatform Information: " + BuildPlatformString());
            
            msg.Append("\n\nDisribution Information:\n\n");
            
            Hashtable lsb = LsbVersionInfo.Harvest;
            
            foreach(string lsbfile in lsb.Keys) {
                msg.Append("[" + lsbfile + "]\n");
                msg.Append(lsb[lsbfile] + "\n");
            }
            
            return msg.ToString();
        }
        
        private string BuildPlatformString()
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.Arguments = "-sirom";
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;
            startInfo.UseShellExecute = false;
            
            foreach(string unameprog in new string [] { 
                "/usr/bin/uname", "/bin/uname", "/usr/local/bin/uname",
                "/sbin/uname", "/usr/sbin/uname", "/usr/local/sbin/uname"}) {
                try {
                    startInfo.FileName = unameprog;
                    Process uname = Process.Start(startInfo);
                    return uname.StandardOutput.ReadLine().Trim();
                } catch(Exception) {
                    continue;
                }
            }
            
            return null;
        }
        
        private class LsbVersionInfo
        {
            private string [] filesToCheck = {
                "*-release",
                "slackware-version",
                "debian_version"
            };
            
            private Hashtable harvest = new Hashtable(); 
            
            public LsbVersionInfo()
            {
                foreach(string pattern in filesToCheck) {
                    foreach(string filename in Directory.GetFiles("/etc/", pattern)) {
                        using(FileStream fs = File.OpenRead(filename)) {
                            harvest[filename] = (new StreamReader(fs)).ReadToEnd();
                        }
                    }
                }
            }
            
            public Hashtable Findings {
                get { return harvest; }
            }
            
            public static Hashtable Harvest {
                get { return (new LsbVersionInfo()).Findings; }
            }
        }
    }
}
