""" Set up and run a single wikibot instance.

    Copyright (C) 2009 Darrell Karbott

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 2.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

    Author: djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks
"""

import os
from ConfigParser import ConfigParser

from fcpclient import FCPClient, get_usk_hash
from fcpconnection import FCPConnection, PolledSocket
from requestqueue import RequestRunner
from bundlecache import is_writable

from fmsstub import FMSStub

from fmsbot import FMSBotRunner, run_event_loops
from wikibot import WikiBot


def read_fnwiki_cfg(cfg_file):
    """ Quick and dirty helper w/o hg deps. to read cfg file."""
    parser = ConfigParser()
    parser.read(cfg_file)
    if not parser.has_section('default'):
        raise IOError("Can't read default section of config file?")

    # Hmmm some param key strings are different than config.py.
    return {'WIKI_ROOT':parser.get('default', 'wiki_root'),
            'SITE_NAME':parser.get('default', 'site_name'),
            'SITE_DEFAULT_FILE':parser.get('default', 'default_file'),
            'FMS_GROUP':parser.get('default', 'wiki_group'),
            # Only the part before '@'.
            'FMS_ID':parser.get('default', 'wiki_server_id').split('@')[0],
            'WIKI_REPO_USK':parser.get('default', 'wiki_repo_usk')}

# LATER: load from a config file
def get_params():
    """ Return the parameters to run a WikiBot. """

    # Directory containing all bot related stuff.
    base_dir = '/tmp/wikibots'

    # File containing the private SSK key.
    key_file_fmt = key_file = '~/wikibot_key_%s.txt'

    # FCP info
    fcp_host = '127.0.0.1'
    fcp_port = 9481

    # FMS info
    fms_host = '127.0.0.1'
    fms_port = 1119
    # NOTE: fms id for bot is read from fnwiki.cfg.

    # Latest known repo usk index
    index_hint = 0

    # vebosity of logging output (NOT FCP 'Verbosity')
    verbosity = 5

    # MUST exist
    tmp_dir = os.path.join(base_dir, '__wikibot_tmp__')
    # MUST exist and contain wikitext hg repo.
    repo_dir = os.path.join(base_dir, 'hgrepo')
    # MUST exist
    bot_storage_dir = os.path.join(base_dir, 'bot_storage')

    #----------------------------------------------------------#
    assert is_writable(tmp_dir)
    assert os.path.exists(os.path.join(repo_dir, '.hg'))

    params = read_fnwiki_cfg(os.path.join(repo_dir, 'fnwiki.cfg'))

    # MUST contain SSK private key
    key_file = key_file_fmt % get_usk_hash(params['WIKI_REPO_USK'])
    print "Read insert key from: %s" % key_file

    # Load private key for the repo from a file..
    insert_ssk = open(os.path.expanduser(key_file), 'rb').read().strip()
    assert insert_ssk.startswith('SSK@')
    # Raw SSK insert key.
    insert_ssk = insert_ssk.split('/')[0].strip()

    # Make insert URI from request URI in config file.
    human = '/'.join(params['WIKI_REPO_USK'].split('/')[1:])
    insert_uri = 'U' + insert_ssk[1:] + '/' + human

    # Then invert the request_uri from it.
    print "Inverting public key from private one..."
    request_uri = FCPClient.connect(fcp_host, fcp_port). \
                  get_request_uri(insert_uri)
    print request_uri
    if get_usk_hash(request_uri) != get_usk_hash(params['WIKI_REPO_USK']):
        print "The insert SSK doesn't match WIKI_REPO_USK in fnwiki.cfg!"
        assert False

    # LATER: Name convention.
    # USK@/foo.wikitext.R1/0 -- wiki source
    # USK@/foo/0 -- freesite

    #print "Reading latest index from Freenet... This can take minutes."
    #index = prefetch_usk(FCPClient.connect(fcp_host, fcp_port),
    #                     request_uri)
    #insert_uri = get_usk_for_usk_version(insert_uri, index)
    #request_uri = get_usk_for_usk_version(request_uri, index) # needed?


    # Hmmmm... freesite index is read from 'I_<n>' tags in
    # repo. There is no way to set it.
    params.update({
        # FCP 2.0
        'MaxRetries':3,
        'PriorityClass':1,
        #'DontCompress':True,
        'Verbosity':1023, # MUST set this to get progress messages.

        # FCPConnection / RequestRunner
        'FCP_HOST':fcp_host,
        'FCP_PORT':fcp_port,
        'FCP_POLL_SECS':0.25,
        'N_CONCURRENT':4,
        'CANCEL_TIME_SECS': 7 * 60,

        # FMSBotRunner
        'FMS_HOST':fms_host,
        'FMS_PORT':fms_port,
        'FMS_POLL_SECS': 3 * 60,
        'BOT_STORAGE_DIR':bot_storage_dir,

        # WikiBot
        'FMS_NOTIFY_GROUP':'infocalypse.notify', # extra group to notify.
        'LATEST_INDEX':index_hint, # Just a hint, it is also stored in shelve db
        'SITE_KEY':insert_ssk,
        'INSERT_URI':insert_uri,
        'REQUEST_URI':request_uri,
        'VERBOSITY':verbosity,
        'TMP_DIR':tmp_dir,
        'NO_SEARCH':False, # REQUIRED
        'USK_HASH':get_usk_hash(request_uri),
        'FNPUSH_COALESCE_SECS':60, # Time to wait before pushing
        'SITE_COALESCE_SECS':60, # Time to wait before inserting.
        'NOTIFY_COALESCE_SECS':60, # Time 2w8b4 sending fms repo update msg
        'COMMIT_COALESCE_SECS':-1, # Hack to force immediate commit
        'FMS_TRUST_CACHE_SECS': 1 * 60 * 60,
        'FMS_MIN_TRUST':55, # peer message trust
        'NONE_TRUST':49, # i.e. disable posting for 'None' peer msg trust
        'REPO_DIR':repo_dir,

        # Only uncomment for testing.
        #'MSG_SPOOL_DIR':'/tmp/fake_msgs',
        })

    return params

def run_wikibot(params):
    """ Setup an FMSBotRunner and run a single WikiBot instance in it. """

    # Setup RequestQueue for FCP requests.
    async_socket = PolledSocket(params['FCP_HOST'], params['FCP_PORT'])
    request_runner = RequestRunner(FCPConnection(async_socket, True),
                                   params['N_CONCURRENT'])

    # Setup FMSBotRunner to house the WikiBot.
    bot_runner = FMSBotRunner(params)
    if 'MSG_SPOOL_DIR' in params:
        print "READING MESSAGES FROM SPOOL DIR INSTEAD OF FMS!"

        bot_runner.nntp = FMSStub(params['MSG_SPOOL_DIR'],
                                  params['FMS_GROUP'])

    # Install a single WikiBot instance.
    wiki_bot = WikiBot('wikibot_' + params['USK_HASH'],
                       params, request_runner)
    bot_runner.register_bot(wiki_bot, (params['FMS_GROUP'], ))

    # Initialize the FMSBotRunner
    bot_runner.startup()

    # Run until there's an error on the FCP socket or
    # the FMSBotRunner shuts down.
    run_event_loops(bot_runner, request_runner,
                    params['FMS_POLL_SECS'],
                    params['FCP_POLL_SECS'],
                    wiki_bot.log) # Hmmm... (ab)use WikiBot log.



if __name__ == "__main__":
    run_wikibot(get_params())