private void resetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string[] candidateTables = DB.Tables;
            if (candidateTables.Length == 0)
            {
                MessageBox.Show("It appears as though the ATT system has not yet been initialized. This indicates an error somewhere in the configuration.");
                return;
            }

            DynamicForm f = new DynamicForm("Reset ATT System", DynamicForm.CloseButtons.OkCancel);
            f.AddListBox("Tables to KEEP:", candidateTables, null, SelectionMode.MultiExtended, "keep", true, "Select the tables that you wish to keep.");
            f.GetControl<ListBox>("keep").SelectedItem = null;
            if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                Set<string> tablesToKeep = new Set<string>(f.GetValue<ListBox.SelectedObjectCollection>("keep").Cast<string>().ToArray());
                if (candidateTables.Where(t => !tablesToKeep.Contains(t)).Count() > 0 && MessageBox.Show("This will permanently delete data from the database. Proceed?", "WARNING", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                    try
                    {
                        ATT.Configuration.Reset(tablesToKeep);
                        _logWriter.Clear();
                        RefreshAll();
                    }
                    catch (Exception ex) { Console.Out.WriteLine("Error resetting the ATT system:  " + ex.Message); }
            }
        }
        private void deleteGeographicDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Shapefile[] shapefiles = Shapefile.GetAll().ToArray();
            if (shapefiles.Length == 0)
                MessageBox.Show("No geographic data available for deletion.");
            else
            {
                DynamicForm f = new DynamicForm("Select geographic data to delete...", DynamicForm.CloseButtons.OkCancel);
                f.AddListBox("Geographic data:", shapefiles, null, SelectionMode.MultiExtended, "shapefiles", true);
                if (f.ShowDialog() == DialogResult.OK)
                {
                    Shapefile[] selectedShapefiles = f.GetValue<System.Windows.Forms.ListBox.SelectedObjectCollection>("shapefiles").Cast<Shapefile>().ToArray();
                    if (selectedShapefiles.Length > 0 && MessageBox.Show("Are you sure you want to delete " + selectedShapefiles.Length + " shapefile(s)?", "Confirm delete", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        int shapefilesDeleted = 0;
                        foreach (Shapefile shapefile in selectedShapefiles)
                        {
                            List<Area> areasForShapefile = Area.GetForShapefile(shapefile);
                            List<DiscreteChoiceModel> modelsForShapefile = areasForShapefile.SelectMany(a => DiscreteChoiceModel.GetForArea(a, false)).Union(DiscreteChoiceModel.GetAll(false).Where(m => m is IFeatureBasedDCM && (m as IFeatureBasedDCM).Features.Any(feat => feat.TrainingResourceId == shapefile.Id.ToString() || feat.PredictionResourceId == shapefile.Id.ToString()))).ToList();
                            List<Prediction> predictionsForShapefile = modelsForShapefile.SelectMany(m => Prediction.GetForModel(m, true)).ToList();
                            if (modelsForShapefile.Count > 0 || predictionsForShapefile.Count > 0)
                                if (MessageBox.Show("The shapefile \"" + shapefile + "\" is associated with " + modelsForShapefile.Count + " model(s) and " + predictionsForShapefile.Count + " prediction(s), which must be deleted before the shapefile can be deleted. Delete them now?", "Delete models and predictions?", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                                {
                                    foreach (Prediction prediction in predictionsForShapefile)
                                        prediction.Delete();

                                    foreach (DiscreteChoiceModel model in modelsForShapefile)
                                        model.Delete();
                                }
                                else
                                    continue;

                            try
                            {
                                shapefile.Delete();
                                ++shapefilesDeleted;
                            }
                            catch (Exception ex) { Console.Out.WriteLine("Error deleting shapefile \"" + shapefile.Name + "\":  " + ex.Message); }
                        }

                        Console.Out.WriteLine("Deleted " + shapefilesDeleted + " shapefile(s)");

                        if (shapefilesDeleted > 0)
                            RefreshAll();
                    }
                }
            }
        }
        private void manageStoredImportersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Importer[] storedImporters = Importer.GetAll().ToArray();

            Thread t = new Thread(new ThreadStart(() =>
                {
                    DialogResult manageDialogResult = System.Windows.Forms.DialogResult.OK;
                    bool refreshStoredImporters = false;
                    while (manageDialogResult == System.Windows.Forms.DialogResult.OK)
                    {
                        if (refreshStoredImporters)
                        {
                            storedImporters = Importer.GetAll().ToArray();
                            refreshStoredImporters = false;
                        }

                        DynamicForm f = new DynamicForm("Stored importers...", DynamicForm.CloseButtons.OkClose);
                        f.AddListBox("Importers:", storedImporters, null, SelectionMode.MultiExtended, "importers", true);
                        f.AddDropDown("Action:", Enum.GetValues(typeof(ManageImporterAction)), null, "action", false);
                        if ((manageDialogResult = f.ShowDialog()) == System.Windows.Forms.DialogResult.OK)
                        {
                            ManageImporterAction action = f.GetValue<ManageImporterAction>("action");

                            if (action == ManageImporterAction.Load)
                            {
                                DynamicForm df = new DynamicForm("Select importer source...", DynamicForm.CloseButtons.OkCancel);
                                df.AddTextBox("Path:", ATT.Configuration.ImportersLoadDirectory, 75, "path", addFileBrowsingButtons: true, fileFilter: "ATT importers|*.attimp", initialBrowsingDirectory: ATT.Configuration.ImportersLoadDirectory);
                                if (df.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                                {
                                    string path = df.GetValue<string>("path");
                                    string[] importerPaths = null;
                                    if (Directory.Exists(path))
                                        importerPaths = Directory.GetFiles(path, "*.attimp", SearchOption.TopDirectoryOnly);
                                    else if (File.Exists(path))
                                        importerPaths = new string[] { path };

                                    if (importerPaths != null)
                                    {
                                        BinaryFormatter bf = new BinaryFormatter();
                                        foreach (string importerPath in importerPaths)
                                            using (FileStream fs = new FileStream(importerPath, FileMode.Open, FileAccess.Read))
                                            {
                                                try
                                                {
                                                    Importer importer = bf.Deserialize(fs) as Importer;

                                                    string absolutePath = importer.Path;
                                                    int relativizationIdEnd = importer.RelativePath.IndexOf('}');
                                                    string relativizationId = importer.RelativePath.Substring(0, relativizationIdEnd + 1).Trim('{', '}');
                                                    if (!string.IsNullOrWhiteSpace(relativizationId))
                                                    {
                                                        PathRelativizationId pathRelativizationId = (PathRelativizationId)Enum.Parse(typeof(PathRelativizationId), relativizationId);
                                                        string relativeTrailingPath = importer.RelativePath.Substring(relativizationIdEnd + 1).Trim(Path.DirectorySeparatorChar);
                                                        if (pathRelativizationId == PathRelativizationId.EventDirectory)
                                                            absolutePath = Path.Combine(ATT.Configuration.EventsImportDirectory, relativeTrailingPath);
                                                        else if (pathRelativizationId == PathRelativizationId.IncidentDirectory)
                                                            absolutePath = Path.Combine(ATT.Configuration.IncidentsImportDirectory, relativeTrailingPath);
                                                        else if (pathRelativizationId == PathRelativizationId.ShapefileDirectory)
                                                            absolutePath = Path.Combine(ATT.Configuration.PostGisShapefileDirectory, relativeTrailingPath);
                                                        else
                                                            throw new NotImplementedException("Unrecognized path relativization id:  " + pathRelativizationId);
                                                    }

                                                    importer.Path = absolutePath;
                                                    importer.Save(false);
                                                    fs.Close();
                                                    refreshStoredImporters = true;
                                                }
                                                catch (Exception ex)
                                                {
                                                    Console.Out.WriteLine("Importer import failed:  " + ex.Message);
                                                }
                                            }
                                    }
                                }
                            }
                            else
                            {
                                string exportDirectory = null;
                                foreach (Importer importer in f.GetValue<System.Windows.Forms.ListBox.SelectedObjectCollection>("importers"))
                                    if (action == ManageImporterAction.Delete)
                                    {
                                        importer.Delete();
                                        refreshStoredImporters = true;
                                    }
                                    else if (action == ManageImporterAction.Edit)
                                    {
                                        Dictionary<string, object> updateKeyValue = new Dictionary<string, object>();
                                        DynamicForm updateForm = new DynamicForm("Update importer \"" + importer + "\"...", DynamicForm.CloseButtons.OkCancel);
                                        importer.GetUpdateRequests(new Importer.UpdateRequestDelegate((itemName, currentValue, possibleValues, id) =>
                                            {
                                                itemName += ":";

                                                if (possibleValues != null)
                                                    updateForm.AddDropDown(itemName, possibleValues.ToArray(), currentValue, id, false);
                                                else if (currentValue is string)
                                                    updateForm.AddTextBox(itemName, currentValue as string, -1, id);
                                                else if (currentValue is int)
                                                    updateForm.AddNumericUpdown(itemName, (int)currentValue, 0, int.MinValue, int.MaxValue, 1, id);
                                                else if (currentValue != null)
                                                    throw new NotImplementedException("Cannot dynamically generate form for update request");

                                                updateKeyValue.Add(id, currentValue);
                                            }));

                                        if (updateForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                                        {
                                            foreach (string updateKey in updateKeyValue.Keys.ToArray())
                                                updateKeyValue[updateKey] = updateForm.GetValue<object>(updateKey);

                                            importer.Update(updateKeyValue);
                                            importer.Save(true);
                                            refreshStoredImporters = true;
                                        }
                                    }
                                    else if (action == ManageImporterAction.Store)
                                    {
                                        if (exportDirectory == null)
                                            exportDirectory = LAIR.IO.Directory.PromptForDirectory("Select export directory...", ATT.Configuration.ImportersLoadDirectory);

                                        if (Directory.Exists(exportDirectory))
                                        {
                                            try
                                            {
                                                BinaryFormatter bf = new BinaryFormatter();
                                                using (FileStream fs = new FileStream(Path.Combine(exportDirectory, ReplaceInvalidFilenameCharacters(importer.ToString() + ".attimp")), FileMode.Create, FileAccess.ReadWrite))
                                                {
                                                    bf.Serialize(fs, importer);
                                                    fs.Close();
                                                    Console.Out.WriteLine("Exported \"" + importer + "\".");
                                                }
                                            }
                                            catch (Exception ex)
                                            {
                                                Console.Out.WriteLine("Importer export failed:  " + ex.Message);
                                            }
                                        }
                                    }
                                    else if (action == ManageImporterAction.Run)
                                    {
                                        Console.Out.WriteLine("Running importer \"" + importer + "\"...");
                                        try { importer.Import(); }
                                        catch (Exception ex)
                                        {
                                            Console.Out.WriteLine("Import failed:  " + ex.Message);
                                        }
                                    }
                                    else
                                        MessageBox.Show("Unrecognized action:  " + action);
                            }
                        }
                    }

                    // might have imported/created an area
                    RefreshPredictionAreas();
                }));

            t.SetApartmentState(ApartmentState.STA);
            t.Start();
        }
        private void collapseIncidentTypesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DynamicForm f = new DynamicForm("Collapse incident types...", DynamicForm.CloseButtons.OkCancel);

            f.AddDropDown("Area:", Area.GetAll().ToArray(), null, "area", true, new Action<object, EventArgs>((o, args) =>
                {
                    ListBox typesList = f.GetControl<ListBox>("types");
                    if (typesList != null)
                    {
                        typesList.Items.Clear();

                        Area selectedArea = (o as ComboBox).SelectedItem as Area;
                        if (selectedArea != null)
                            foreach (string type in Incident.GetUniqueTypes(DateTime.MinValue, DateTime.MaxValue, selectedArea))
                                typesList.Items.Add(type);
                    }
                }));

            Area area = f.GetValue<Area>("area") as Area;
            if (area == null)
                MessageBox.Show("No areas available to collapse incidents for.");
            else
            {
                f.AddListBox("Types:", Incident.GetUniqueTypes(DateTime.MinValue, DateTime.MaxValue, area).ToArray(), null, SelectionMode.MultiExtended, "types", true);
                f.AddTextBox("Collapsed type:", null, 50, "collapsed");

                if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    area = f.GetValue<Area>("area");

                    List<string> types = new List<string>();
                    foreach (string type in f.GetValue<System.Windows.Forms.ListBox.SelectedObjectCollection>("types"))
                        types.Add(type);

                    string collapsedType = f.GetValue<string>("collapsed").Trim();

                    if (area == null)
                        MessageBox.Show("Must select an area.");
                    else if (types.Count <= 1)
                        MessageBox.Show("Must select two or more types to collapse.");
                    else if (string.IsNullOrWhiteSpace(collapsedType))
                        MessageBox.Show("Must enter a collapsed type name.");
                    else if (MessageBox.Show("Are you sure you want to collapse these incident types?", "Collapse?", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                        Incident.Collapse(area, types, collapsedType);
                }
            }
        }