
import numarray

import stackless


from PyObjCTools import NibClassBuilder
from AppKit import NSDocumentController, NSApplicationMain
from Foundation import *
from objc import YES, NO

from ZODB import FileStorage, DB
import persistent

import mobile

import sys
sys.path.append('/Users/dp/Development/svn.undefined.org/DrawBot')

import PyInterpreterDocument
import PyDETextView
import DrawingPrimitives

import math

import os
import thread
## This doesn't work inside a standalone bundle so just put it in . for now
#database = DB(FileStorage.FileStorage(os.path.join(*os.path.split(mobile.__file__)[:-1] + ('test.db', ))))
dbfile = os.path.expanduser('~/Documents/MobileDB/mobile.db')
if not os.path.exists(dbfile):
    import bootstrapdb
    database = bootstrapdb.bootstrap()
else:
    database = DB(FileStorage.FileStorage(dbfile))
connection = database.open()
root = connection.root()


import coreaudio


NibClassBuilder.extractClasses("MainMenu")

"""
def callback(self):
    from math import sin
    offset = getattr(self, 'offset', 0)
    frequency = getattr(self, 'frequency', 220.0)
    self.offset += 512
    if frequency > 880.0:
        self.direction = -1
    if frequency < 40.0:
        self.direction = 1
    self.frequency += getattr(self, 'direction', 1)
    return [
        sin(3.1415*frequency/44100.0*(frame+offset))
        for frame in xrange(512)
    ]
"""


audioPlayer = None


# class defined in Mobile.nib
class MobileController(NibClassBuilder.AutoBaseClass):
    # the actual base class is NSObject
    # The following outlets are added to the class:
    # table -- the top table
    # panel -- the bottom detail inspector
    parent = None
    def applicationDidFinishLaunching_(self, aNotification):
        self.root = PythonBrowserModel.alloc().initWithObject_andParent_andController_(root, self, self.panel)
        self.table.setDataSource_(self.root)
        self.table.setDelegate_(self.root)
        ## Kick off python thread initialization so we don't crash later when we get called back on a new thread
        thread.start_new_thread(lambda: None, ())
        self.newItem.setEnabled_(NO)
        self.saveItem.setEnabled_(NO)

    def makeInterpreter(self):
        ## Make the PyInterpreter window
        sdc = NSDocumentController.sharedDocumentController()
        typ = sdc.typeFromFileExtension_('pyint')
        d = sdc.makeUntitledDocumentOfType_(typ)
        d.makeWindowControllers()
        sdc.addDocument_(d)
        d.showWindows()
        self.interpreter = d.windowControllers()[0].window().delegate()
        self.locals = self.interpreter._console.locals
        self.locals.update(root)

    def applicationWillTerminate_(self, aNotification):
        global audioPlayer
        if audioPlayer is not None:
            coreaudio.stopAudio(audioPlayer)

    def setSaveAction_(self, action):
        self.saveAction = action

    def setNewAction_(self, action):
        self.newAction = action
        self.newItem.setEnabled_(YES)

    def doSave_(self, sender):
        if self.saveAction is not None:
            self.saveAction(sender)

    def doNew_(self, sender):
        if self.newAction is not None:
            self.newAction(sender)

    def startAudio_(self, sender):
        self.audio = root['audio']
        self.audio.callback()
        coreaudio.installAudioCallback(self.audio)

    def stopAudio_(self, sender):
        coreaudio.stopAudio(self.audio)


## Gratuitously stolen from PythonBrowser example
class PythonBrowserModel(NSObject):
    """This is a delegate as well as a data source for NSOutlineViews.
    """
    def initWithObject_andParent_andController_(self, obj, parent, controller):
        self = self.init()
        self.controller = controller
        self.root = PythonItem.alloc().initWithObject_andParent_forName_(obj, parent, 'localhost')
        return self

    # NSOutlineViewDataSource  methods

    def outlineView_numberOfChildrenOfItem_(self, view, item):
        if item is None:
            return 1
            item = self.root
        return len(item)

    def outlineView_child_ofItem_(self, view, child, item):
        if item is None:
            return self.root
            item = self.root
        return item.getChild(child)

    def outlineView_isItemExpandable_(self, view, item):
        return item.isExpandable()

    def outlineView_objectValueForTableColumn_byItem_(self, view, col, item):
        print item
        return getattr(item, col.identifier())

    def outlineView_setObjectValue_forTableColumn_byItem_(self, view, value, col, item):
        pass

    # delegate method
    def outlineView_shouldEditTableColumn_item_(self, view, col, item):
        return item.isEditable()

    def outlineView_shouldSelectItem_(self, outline, item):
        self.controller.inspectItem_(item)
        return YES


typeRegistry = {}


class PythonItem(NSObject):
    def initWithObject_andParent_forName_(self, obj, parent, name):
        constructorType = typeRegistry.get(type(obj), None)
        if constructorType is not None and constructorType is not type(self):
            return constructorType.alloc().initWithObject_andParent_forName_(obj, parent, name)
        self = self.init()
        self._children = {}
        self.obj = obj
        self.parent = parent
        self.name = name
        return self

    def isEditable(self):
        return False

    def isExpandable(self):
        if self.parent is None:
            return True
        return False

    def getChild(self, child):
        if self.parent is None:
            key = self.obj.keys()[child]
            I = PythonItem.alloc().initWithObject_andParent_forName_(self.obj[key], self, key)
            self._children[child] = I
            return I

    def getController(self):
        root = self
        while root.parent is not None:
            root = root.parent
        return root

    def __len__(self):
        return len(self.obj)


class RootItem(PythonItem):
    def isExpandable(self): return True
    def getChild(self, child):
        key = self.obj.keys()[child]
        I = PythonItem.alloc().initWithObject_andParent_forName_(self.obj[key], self, key)
        self._children[child] = I
        return I
typeRegistry[persistent.mapping.PersistentMapping] = RootItem


class MobItem(PythonItem):
    _childItems = ['_methods']
    def isExpandable(self): return True
    def getChild(self, child):
        childKey = self._childItems[child]
        childList = getattr(self.obj, childKey)
        I = PythonItem.alloc().initWithObject_andParent_forName_(childList, self, childKey[1:])
        self._children[child] = I
        return I
    def __len__(self):
        return len(self._childItems)
typeRegistry[mobile.Mobile] = MobItem


class ListItem(PythonItem):
    def isExpandable(self): return True
    def getChild(self, child):
        element = self.obj[child]
        I = PythonItem.alloc().initWithObject_andParent_forName_(element, self, element)
        self._children[child] = I
        return I
typeRegistry[type([])] = ListItem


def calculateName(K):
    name = "untitled"
    I = 2
    while name in K:
        name = "untitled%s" % I
        I += 1
    return name


class PanelController(NibClassBuilder.AutoBaseClass):
    """Controller that mediates between the upper and lower pane of the main window
    """
    # outlets:
    # source
    # actions:
    # compileSource_
    num = 0

    inspectorRegistry = {}
    viewRegistry = {}
    def inspectItem_(self, item):
        print "METHOD TYPE", type(item)
        self.inspecting = item
        method = self.inspectorRegistry.get(type(item), None)
        if method is not None:
            panelName = self.viewRegistry[type(item)]
            self.bottomPanel.setContentView_(getattr(self, panelName))
            method(self, item)

    def inspectMethod(self, item):
        self.inspecting = item
        mobobj = item.parent.parent.obj
        src = mobobj.list(item.obj)
        self.currentString = NSAttributedString.alloc().initWithString_attributes_('\n'.join(src), {})
        ts = self.sourceText.textStorage()
        ts.replaceCharactersInRange_withAttributedString_((0, len(ts.mutableString())), self.currentString)
        self.num += 1
    inspectorRegistry[PythonItem] = inspectMethod
    viewRegistry[PythonItem] = 'sourceView'

    def inspectRoot(self, item):
        self.locals = self.interpreterController._console.locals
        self.locals.update(root)
        item.getController().setNewAction_(self.newInstance_)
    inspectorRegistry[RootItem] = inspectRoot
    viewRegistry[RootItem] = 'interpreterView'

    def newInstance_(self, sender):
        root[calculateName(root.keys())] = mobile.Mobile()

        print "NEW INSTANCE", sender

    def inspectInstance(self, item):
        O = item.obj
        self.drawView.delegate = O
        ## Does it have sound?
        global audioPlayer
        if audioPlayer is not None:
            coreaudio.stopAudio(audioPlayer)
        if hasattr(O, 'callback'):
            coreaudio.installAudioCallback(O)
            audioPlayer = O
        C = item.getController()
        C.setNewAction_(self.newMethod_)
        C.setSaveAction_(self.compileSource_)
    inspectorRegistry[MobItem] = inspectInstance
    viewRegistry[MobItem] = 'instanceView'

    def newMethod_(self, sender):
        mob = self.inspecting.obj
        name = calculateName(mob._methods)
        mob.compile("""def %s(self):
    pass
""" % name)

    def compileSource_(self, sender):
        source = self.sourceText.textStorage().string()
        mobobj = self.inspecting.parent.parent.obj
        mobobj.compile(source)
        get_transaction().commit()
        print "SET SOURCE", source


drawNames = {}
for module in (math, DrawingPrimitives):
    for name in getattr(module, '__all__', None) or dir(module):
        if not name.startswith('_'):
            drawNames[name] = getattr(module, name)


class MobileDrawView(NibClassBuilder.AutoBaseClass):
    delegate = None

    def dirty_(self, _):
        self.display()

    def drawRect_(self, aRect):
        D = self.delegate
        if D is not None:
            M = getattr(D, 'step', None)
            if M is not None:
                M.im_func.func_globals.update(drawNames)
                result = M()
                if result is not None:
                    NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
                        result,
                        self,
                        self.dirty_,
                        None,
                        NO)


if __name__ == "__main__":
    # pass control to the AppKit
    import sys
    sys.exit(NSApplicationMain([]))


