from __future__ import generators
# Do the best we can to completely obliterate a field from Outlook!

from win32com.client import Dispatch, constants
import pythoncom
import os, sys

from win32com.mapi import mapi
from win32com.mapi.mapitags import *

import mapi_driver

def DeleteField_Outlook(folder, name):
    name = name.lower()
    entries = folder.Items
    num_outlook = 0
    entry = entries.GetFirst()
    while entry is not None:
        up = entry.UserProperties
        num_props = up.Count
        for i in range(num_props):
            if up[i+1].Name.lower()==name:
                num_outlook += 1
                entry.UserProperties.Remove(i+1)
                entry.Save()
                break
        entry = entries.GetNext()
    return num_outlook

def DeleteField_MAPI(driver, folder, name):
    # OK - now try and wipe the field using MAPI.
    propIds = folder.GetIDsFromNames(((mapi.PS_PUBLIC_STRINGS,name),), 0)
    if PROP_TYPE(propIds[0])==PT_ERROR:
        print "No such field '%s' in folder" % (name,)
        return 0
    assert propIds[0] == PROP_TAG( PT_UNSPECIFIED, PROP_ID(propIds[0]))
    num_mapi = 0
    for item in driver.GetAllItems(folder):
        # DeleteProps always says"success" - so check to see if it
        # actually exists just so we can count it.
        hr, vals = item.GetProps(propIds)
        if hr==0: # We actually have it
            hr, probs = item.DeleteProps(propIds)
            if  hr == 0:
                item.SaveChanges(mapi.MAPI_DEFERRED_ERRORS)
                num_mapi += 1
    return num_mapi

def DeleteField_Folder(driver, folder, name):
    propIds = folder.GetIDsFromNames(((mapi.PS_PUBLIC_STRINGS,name),), 0)
    if PROP_TYPE(propIds[0])!=PT_ERROR:
        hr, vals = folder.GetProps(propIds)
        if hr==0: # We actually have it
            hr, probs = folder.DeleteProps(propIds)
            if  hr == 0:
                folder.SaveChanges(mapi.MAPI_DEFERRED_ERRORS)
                return 1
    return 0

def CountFields(folder):
    fields = {}
    entries = folder.Items

    entry = entries.GetFirst()
    while entry is not None:
        ups = entry.UserProperties
        num_props = ups.Count
        for i in range(num_props):
            name = ups.Item(i+1).Name
            fields[name] = fields.get(name, 0)+1
        entry = entries.GetNext()
    for name, num in fields.items():
        print name, num

def ShowFields(folder, field_name):
    field_name = field_name.lower()
    entries = folder.Items
    entry = entries.GetFirst()
    while entry is not None:
        ups = entry.UserProperties
        num_props = ups.Count
        for i in range(num_props):
            up = ups[i+1]
            name = up.Name
            if name.lower()==field_name:
                subject = entry.Subject.encode("mbcs", "replace")
                print "%s: %s (%d)" % (subject, up.Value, up.Type)
        entry = entries.GetNext()

def usage(driver):
    folder_doc = driver.GetFolderNameDoc()
    msg = """\
Usage: %s [-f foldername -f ...] [-d] [-s] [FieldName ...]
-f - Run over the specified folders (default = Inbox)
-d - Delete the named fields
  --no-outlook - Don't delete via the Outlook UserProperties API
  --no-mapi - Don't delete via the extended MAPI API
  --no-folder - Don't attempt to delete the field from the folder itself
-s - Show message subject and field value for all messages with field
-n - Show top-level folder names and exit

If no options are given, prints a summary of field names in the folders.

%s
Use the -n option to see all top-level folder names from all stores.""" \
        % (os.path.basename(sys.argv[0]), folder_doc)
    print msg


def main():
    driver = mapi_driver.MAPIDriver()

    import getopt
    try:
        opts, args = getopt.getopt(sys.argv[1:],
                                   "dnsf:",
                                   ["no-mapi", "no-outlook", "no-folder"])
    except getopt.error, e:
        print e
        print
        usage(driver)
        sys.exit(1)
    delete = show = False
    do_mapi = do_outlook = do_folder = True
    folder_names = []
    for opt, opt_val in opts:
        if opt == "-d":
            delete = True
        elif opt == "-s":
            show = True
        elif opt == "-f":
            folder_names.append(opt_val)
        elif opt == "--no-mapi":
            do_mapi = False
        elif opt == "--no-outlook":
            do_outlook = False
        elif opt == "--no-folder":
            do_folder = False
        elif opt == "-n":
            driver.DumpTopLevelFolders()
            sys.exit(1)
        else:
            print "Invalid arg"
            return

    if not folder_names:
        folder_names = ["Inbox"] # Assume this exists!
    if not args:
        print "No args specified - dumping all unique UserProperty names,"
        print "and the count of messages they appear in"
    outlook = None
    for folder_name in folder_names:
        try:
            folder = driver.FindFolder(folder_name)
        except ValueError, details:
            print details
            print "Ignoring folder '%s'" % (folder_name,)
            continue
        print "Processing folder '%s'" % (folder_name,)
        if not args:
            outlook_folder = driver.GetOutlookFolder(folder)
            CountFields(outlook_folder)
            continue
        for field_name in args:
            if show:
                outlook_folder = driver.GetOutlookFolder(folder)
                ShowFields(outlook_folder, field_name)
            if delete:
                print "Deleting field", field_name
                if do_outlook:
                    outlook_folder = driver.GetOutlookFolder(folder)
                    num = DeleteField_Outlook(outlook_folder, field_name)
                    print "Deleted", num, "field instances from Outlook"
                if do_mapi:
                    num = DeleteField_MAPI(driver, folder, field_name)
                    print "Deleted", num, "field instances via MAPI"
                if do_folder:
                    num = DeleteField_Folder(driver, folder, field_name)
                    if num:
                        print "Deleted property from folder"
                    else:
                        print "Could not find property to delete in the folder"

##        item = folder.Items.Add()
##        props = item.UserProperties
##        prop=props.Add("TestInt",3 , True, 1)
##        prop.Value=66
##        item.Save()

if __name__=='__main__':
    main()
