# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import logging
import os
from StringIO import StringIO
import sys

from appengine_wrappers import webapp
from appengine_wrappers import memcache
from appengine_wrappers import urlfetch

from api_data_source import APIDataSource
from api_list_data_source import APIListDataSource
from appengine_blobstore import AppEngineBlobstore
from in_memory_object_store import InMemoryObjectStore
from appengine_url_fetcher import AppEngineUrlFetcher
from branch_utility import BranchUtility
from example_zipper import ExampleZipper
from compiled_file_system import CompiledFileSystem
import compiled_file_system as compiled_fs
from github_file_system import GithubFileSystem
from intro_data_source import IntroDataSource
from known_issues_data_source import KnownIssuesDataSource
from local_file_system import LocalFileSystem
from memcache_file_system import MemcacheFileSystem
from reference_resolver import ReferenceResolver
from samples_data_source import SamplesDataSource
from server_instance import ServerInstance
from sidenav_data_source import SidenavDataSource
from subversion_file_system import SubversionFileSystem
from template_data_source import TemplateDataSource
from third_party.json_schema_compiler.model import UnixName
import url_constants

# Increment this version to force the server to reload all pages in the first
# cron job that is run.
_VERSION = 1

# The default channel to serve docs for if no channel is specified.
_DEFAULT_CHANNEL = 'stable'

BRANCH_UTILITY_MEMCACHE = InMemoryObjectStore('branch_utility')
BRANCH_UTILITY = BranchUtility(url_constants.OMAHA_PROXY_URL,
                               AppEngineUrlFetcher(None),
                               BRANCH_UTILITY_MEMCACHE)

GITHUB_MEMCACHE = InMemoryObjectStore('github')
GITHUB_FILE_SYSTEM = GithubFileSystem(
    AppEngineUrlFetcher(url_constants.GITHUB_URL),
    GITHUB_MEMCACHE,
    AppEngineBlobstore())
GITHUB_COMPILED_FILE_SYSTEM = CompiledFileSystem.Factory(GITHUB_FILE_SYSTEM,
                                                         GITHUB_MEMCACHE)

EXTENSIONS_PATH = 'chrome/common/extensions'
DOCS_PATH = 'docs'
API_PATH = 'api'
TEMPLATE_PATH = DOCS_PATH + '/templates'
INTRO_PATH = TEMPLATE_PATH + '/intros'
ARTICLE_PATH = TEMPLATE_PATH + '/articles'
PUBLIC_TEMPLATE_PATH = TEMPLATE_PATH + '/public'
PRIVATE_TEMPLATE_PATH = TEMPLATE_PATH + '/private'
EXAMPLES_PATH = DOCS_PATH + '/examples'
JSON_PATH = TEMPLATE_PATH + '/json'

# Global cache of instances because Handler is recreated for every request.
SERVER_INSTANCES = {}

def _GetURLFromBranch(branch):
    if branch == 'trunk':
      return url_constants.SVN_TRUNK_URL + '/src'
    return url_constants.SVN_BRANCH_URL + '/' + branch + '/src'

def _SplitFilenameUnix(base_dir, files):
  return [UnixName(os.path.splitext(f.split('/')[-1])[0]) for f in files]

def _CreateMemcacheFileSystem(branch, branch_memcache):
  svn_url = _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH
  stat_fetcher = AppEngineUrlFetcher(
      svn_url.replace(url_constants.SVN_URL, url_constants.VIEWVC_URL))
  fetcher = AppEngineUrlFetcher(svn_url)
  return MemcacheFileSystem(SubversionFileSystem(fetcher, stat_fetcher),
                            branch_memcache)

_default_branch = BRANCH_UTILITY.GetBranchNumberForChannelName(_DEFAULT_CHANNEL)
APPS_MEMCACHE = InMemoryObjectStore(_default_branch)
APPS_FILE_SYSTEM = _CreateMemcacheFileSystem(_default_branch, APPS_MEMCACHE)
APPS_COMPILED_FILE_SYSTEM = CompiledFileSystem.Factory(
    APPS_FILE_SYSTEM,
    APPS_MEMCACHE).Create(_SplitFilenameUnix, compiled_fs.APPS_FS)

EXTENSIONS_MEMCACHE = InMemoryObjectStore(_default_branch)
EXTENSIONS_FILE_SYSTEM = _CreateMemcacheFileSystem(_default_branch,
                                                   EXTENSIONS_MEMCACHE)
EXTENSIONS_COMPILED_FILE_SYSTEM = CompiledFileSystem.Factory(
    EXTENSIONS_FILE_SYSTEM,
    EXTENSIONS_MEMCACHE).Create(_SplitFilenameUnix, compiled_fs.EXTENSIONS_FS)

KNOWN_ISSUES_DATA_SOURCE = KnownIssuesDataSource(
    InMemoryObjectStore('KnownIssues'),
    AppEngineUrlFetcher(None))

def _MakeInstanceKey(branch, number):
  return '%s/%s' % (branch, number)

def _GetInstanceForBranch(channel_name, local_path):
  branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name)

  # The key for the server is a tuple of |channel_name| with |branch|, since
  # sometimes stable and beta point to the same branch.
  instance_key = _MakeInstanceKey(channel_name, branch)
  instance = SERVER_INSTANCES.get(instance_key, None)
  if instance is not None:
    return instance

  branch_memcache = InMemoryObjectStore(branch)
  file_system = _CreateMemcacheFileSystem(branch, branch_memcache)
  cache_factory = CompiledFileSystem.Factory(file_system, branch_memcache)
  api_list_data_source_factory = APIListDataSource.Factory(cache_factory,
                                                           file_system,
                                                           API_PATH,
                                                           PUBLIC_TEMPLATE_PATH)
  api_data_source_factory = APIDataSource.Factory(
      cache_factory,
      API_PATH)

  # Give the ReferenceResolver a memcache, to speed up the lookup of
  # duplicate $refs.
  ref_resolver_factory = ReferenceResolver.Factory(
      api_data_source_factory,
      api_list_data_source_factory,
      branch_memcache)
  api_data_source_factory.SetReferenceResolverFactory(ref_resolver_factory)
  samples_data_source_factory = SamplesDataSource.Factory(
      channel_name,
      file_system,
      GITHUB_FILE_SYSTEM,
      cache_factory,
      GITHUB_COMPILED_FILE_SYSTEM,
      ref_resolver_factory,
      EXAMPLES_PATH)
  api_data_source_factory.SetSamplesDataSourceFactory(
      samples_data_source_factory)
  intro_data_source_factory = IntroDataSource.Factory(
      cache_factory,
      ref_resolver_factory,
      [INTRO_PATH, ARTICLE_PATH])
  sidenav_data_source_factory = SidenavDataSource.Factory(cache_factory,
                                                          JSON_PATH)
  template_data_source_factory = TemplateDataSource.Factory(
      channel_name,
      api_data_source_factory,
      api_list_data_source_factory,
      intro_data_source_factory,
      samples_data_source_factory,
      KNOWN_ISSUES_DATA_SOURCE,
      sidenav_data_source_factory,
      cache_factory,
      ref_resolver_factory,
      PUBLIC_TEMPLATE_PATH,
      PRIVATE_TEMPLATE_PATH)
  example_zipper = ExampleZipper(file_system,
                                 cache_factory,
                                 DOCS_PATH)

  instance = ServerInstance(template_data_source_factory,
                            example_zipper,
                            cache_factory)