docs: def _write_html_pages(root, tlobjects, methods, layer, input_res):
      """
      Generates the documentation HTML files from from ``scheme.tl``
      to ``/methods`` and ``/constructors``, etc.
      """
      # Save 'Type: [Constructors]' for use in both:
      # * Seeing the return type or constructors belonging to the same type.
      # * Generating the types documentation, showing available constructors.
      paths = {k: root / v for k, v in (
          ('css', 'css'),
          ('arrow', 'img/arrow.svg'),
          ('search.js', 'js/search.js'),
          ('404', '404.html'),
          ('index_all', 'index.html'),
          ('bot_index', 'botindex.html'),
          ('index_types', 'types/index.html'),
          ('index_methods', 'methods/index.html'),
          ('index_constructors', 'constructors/index.html')
      )}
      paths['default_css'] = 'light'  # docs.<name>.css, local path
      type_to_constructors = defaultdict(list)
      type_to_functions = defaultdict(list)
      for tlobject in tlobjects:
          d = type_to_functions if tlobject.is_function else type_to_constructors
          d[tlobject.result].append(tlobject)
  
      for t, cs in type_to_constructors.items():
          type_to_constructors[t] = list(sorted(cs, key=lambda c: c.name))
  
      methods = {m.name: m for m in methods}
  
      # Since the output directory is needed everywhere partially apply it now
      create_path_for = functools.partial(_get_path_for, root)
      path_for_type = lambda t: root / _get_path_for_type(t)
      bot_docs_paths = []
  
      for tlobject in tlobjects:
          filename = create_path_for(tlobject)
          with DocsWriter(root, filename, path_for_type) as docs:
              docs.write_head(title=tlobject.class_name,
                              css_path=paths['css'],
                              default_css=paths['default_css'])
  
              # Create the menu (path to the current TLObject)
              docs.set_menu_separator(paths['arrow'])
              _build_menu(docs)
  
              # Create the page title
              docs.write_title(tlobject.class_name)
  
              if tlobject.is_function:
                  if tlobject.usability == Usability.USER:
                      start = '<strong>Only users</strong> can'
                  elif tlobject.usability == Usability.BOT:
                      bot_docs_paths.append(filename)
                      start = '<strong>Only bots</strong> can'
                  elif tlobject.usability == Usability.BOTH:
                      bot_docs_paths.append(filename)
                      start = '<strong>Both users and bots</strong> can'
                  else:
                      bot_docs_paths.append(filename)
                      start = \
                          'Both users and bots <strong>may</strong> be able to'
  
                  docs.write_text('{} use this method. <a href="#examples">'
                                  'See code examples.</a>'.format(start))
  
              # Write the code definition for this TLObject
              docs.write_code(tlobject)
              docs.write_copy_button('Copy import to the clipboard',
                                     get_import_code(tlobject))
  
              # Write the return type (or constructors belonging to the same type)
              docs.write_title('Returns' if tlobject.is_function
                               else 'Belongs to', level=3)
  
              generic_arg = next((arg.name for arg in tlobject.args
                                  if arg.generic_definition), None)
  
              if tlobject.result == generic_arg:
                  # We assume it's a function returning a generic type
                  generic_arg = next((arg.name for arg in tlobject.args
                                      if arg.is_generic))
                  docs.write_text('This function returns the result of whatever '
                                  'the result from invoking the request passed '
                                  'through <i>{}</i> is.'.format(generic_arg))
              else:
                  if re.search('^vector<', tlobject.result, re.IGNORECASE):
                      docs.write_text('A list of the following type is returned.')
                      _, inner = tlobject.result.split('<')
                      inner = inner.strip('>')
                  else:
                      inner = tlobject.result
  
                  docs.begin_table(column_count=1)
                  docs.add_row(inner, link=path_for_type(inner))
                  docs.end_table()
  
                  cs = type_to_constructors.get(inner, [])
                  if not cs:
                      docs.write_text('This type has no instances available.')
                  elif len(cs) == 1:
                      docs.write_text('This type can only be an instance of:')
                  else:
                      docs.write_text('This type can be an instance of either:')
  
                  docs.begin_table(column_count=2)
                  for constructor in cs:
                      link = create_path_for(constructor)
                      docs.add_row(constructor.class_name, link=link)
                  docs.end_table()
  
              # Return (or similar types) written. Now parameters/members
              docs.write_title(
                  'Parameters' if tlobject.is_function else 'Members', level=3
              )
  
              # Sort the arguments in the same way they're sorted
              # on the generated code (flags go last)
              args = [
                  a for a in tlobject.sorted_args()
                  if not a.flag_indicator and not a.generic_definition
              ]
  
              if args:
                  # Writing parameters
                  docs.begin_table(column_count=3)
  
                  for arg in args:
                      # Name row
                      docs.add_row(arg.name,
                                   bold=True)
  
                      # Type row
                      friendly_type = 'flag' if arg.type == 'true' else arg.type
                      if arg.is_generic:
                          docs.add_row('!' + friendly_type, align='center')
                      else:
                          docs.add_row(
                              friendly_type, align='center',
                              link=path_for_type(arg.type)
                           )
  
                      # Add a description for this argument
                      docs.add_row(_get_description(arg))
  
                  docs.end_table()
              else:
                  if tlobject.is_function:
                      docs.write_text('This request takes no input parameters.')
                  else:
                      docs.write_text('This type has no members.')
  
              if tlobject.is_function:
                  docs.write_title('Known RPC errors')
                  method_info = methods.get(tlobject.fullname)
                  errors = method_info and method_info.errors
                  if not errors:
                      docs.write_text("This request can't cause any RPC error "
                                      "as far as we know.")
                  else:
                      docs.write_text(
                          'This request can cause {} known error{}:'.format(
                              len(errors), '' if len(errors) == 1 else 's'
                      ))
                      docs.begin_table(column_count=2)
                      for error in errors:
                          docs.add_row('<code>{}</code>'.format(error.name))
                          docs.add_row('{}.'.format(error.description))
                      docs.end_table()
                      docs.write_text('You can import these from '
                                      '<code>telethon.errors</code>.')
  
                  docs.write_title('Example', id='examples')
                  docs.write('''<pre>\
  <strong>from</strong> telethon.sync <strong>import</strong> TelegramClient
  <strong>from</strong> telethon <strong>import</strong> functions, types
  
  <strong>with</strong> TelegramClient(name, api_id, api_hash) <strong>as</strong> client:
      result = client(''')
                  tlobject.as_example(docs, indent=1)
                  docs.write(')\n')
                  if tlobject.result.startswith('Vector'):
                      docs.write('''\
      <strong>for</strong> x <strong>in</strong> result:
          print(x''')
                  else:
                      docs.write('    print(result')
                      if tlobject.result != 'Bool' \
                              and not tlobject.result.startswith('Vector'):
                          docs.write('.stringify()')
  
                  docs.write(')</pre>')
  
              depth = '../' * (2 if tlobject.namespace else 1)
              docs.add_script(src='prependPath = "{}";'.format(depth))
              docs.add_script(path=paths['search.js'])
              docs.end_body()
  
      # Find all the available types (which are not the same as the constructors)
      # Each type has a list of constructors associated to it, hence is a map
      for t, cs in type_to_constructors.items():
          filename = path_for_type(t)
          out_dir = filename.parent
          if out_dir:
              out_dir.mkdir(parents=True, exist_ok=True)
  
          # Since we don't have access to the full TLObject, split the type
          if '.' in t:
              namespace, name = t.split('.')
          else:
              namespace, name = None, t
  
          with DocsWriter(root, filename, path_for_type) as docs:
              docs.write_head(title=snake_to_camel_case(name),
                              css_path=paths['css'],
                              default_css=paths['default_css'])
  
              docs.set_menu_separator(paths['arrow'])
              _build_menu(docs)
  
              # Main file title
              docs.write_title(snake_to_camel_case(name))
  
              # List available constructors for this type
              docs.write_title('Available constructors', level=3)
              if not cs:
                  docs.write_text('This type has no constructors available.')
              elif len(cs) == 1:
                  docs.write_text('This type has one constructor available.')
              else:
                  docs.write_text('This type has %d constructors available.' %
                                  len(cs))
  
              docs.begin_table(2)
              for constructor in cs:
                  # Constructor full name
                  link = create_path_for(constructor)
                  docs.add_row(constructor.class_name, link=link)
              docs.end_table()
  
              # List all the methods which return this type
              docs.write_title('Methods returning this type', level=3)
              functions = type_to_functions.get(t, [])
              if not functions:
                  docs.write_text('No method returns this type.')
              elif len(functions) == 1:
                  docs.write_text('Only the following method returns this type.')
              else:
                  docs.write_text(
                      'The following %d methods return this type as a result.' %
                      len(functions)
                  )
  
              docs.begin_table(2)
              for func in functions:
                  link = create_path_for(func)
                  docs.add_row(func.class_name, link=link)
              docs.end_table()
  
              # List all the methods which take this type as input
              docs.write_title('Methods accepting this type as input', level=3)
              other_methods = sorted(
                  (u for u in tlobjects
                   if any(a.type == t for a in u.args) and u.is_function),
                  key=lambda u: u.name
              )
              if not other_methods:
                  docs.write_text(
                      'No methods accept this type as an input parameter.')
              elif len(other_methods) == 1:
                  docs.write_text(
                      'Only this method has a parameter with this type.')
              else:
                  docs.write_text(
                      'The following %d methods accept this type as an input '
                      'parameter.' % len(other_methods))
  
              docs.begin_table(2)
              for ot in other_methods:
                  link = create_path_for(ot)
                  docs.add_row(ot.class_name, link=link)
              docs.end_table()
  
              # List every other type which has this type as a member
              docs.write_title('Other types containing this type', level=3)
              other_types = sorted(
                  (u for u in tlobjects
                   if any(a.type == t for a in u.args) and not u.is_function),
                  key=lambda u: u.name
              )
  
              if not other_types:
                  docs.write_text(
                      'No other types have a member of this type.')
              elif len(other_types) == 1:
                  docs.write_text(
                      'You can find this type as a member of this other type.')
              else:
                  docs.write_text(
                      'You can find this type as a member of any of '
                      'the following %d types.' % len(other_types))
  
              docs.begin_table(2)
              for ot in other_types:
                  link = create_path_for(ot)
                  docs.add_row(ot.class_name, link=link)
              docs.end_table()
              docs.end_body()
  
      # After everything's been written, generate an index.html per folder.
      # This will be done automatically and not taking into account any extra
      # information that we have available, simply a file listing all the others
      # accessible by clicking on their title
      for folder in ['types', 'methods', 'constructors']:
          _generate_index(root, root / folder, paths)
  
      _generate_index(root, root / 'methods', paths, True,
                      bot_docs_paths)
  
      # Write the final core index, the main index for the rest of files
      types = set()
      methods = []
      cs = []
      for tlobject in tlobjects:
          if tlobject.is_function:
              methods.append(tlobject)
          else:
              cs.append(tlobject)
  
          if not tlobject.result.lower() in CORE_TYPES:
              if re.search('^vector<', tlobject.result, re.IGNORECASE):
                  types.add(tlobject.result.split('<')[1].strip('>'))
              else:
                  types.add(tlobject.result)
  
      types = sorted(types)
      methods = sorted(methods, key=lambda m: m.name)
      cs = sorted(cs, key=lambda c: c.name)
  
      shutil.copy(str(input_res / '404.html'), str(paths['404']))
      _copy_replace(input_res / 'core.html', paths['index_all'], {
          '{type_count}': len(types),
          '{method_count}': len(methods),
          '{constructor_count}': len(tlobjects) - len(methods),
          '{layer}': layer,
      })
  
      def fmt(xs):
          zs = {}  # create a dict to hold those which have duplicated keys
          for x in xs:
              zs[x.class_name] = x.class_name in zs
          return ', '.join(
              '"{}.{}"'.format(x.namespace, x.class_name)
              if zs[x.class_name] and x.namespace
              else '"{}"'.format(x.class_name) for x in xs
          )
  
      request_names = fmt(methods)
      constructor_names = fmt(cs)
  
      def fmt(xs, formatter):
          return ', '.join('"{}"'.format(
              formatter(x)).replace(os.path.sep, '/') for x in xs)
  
      type_names = fmt(types, formatter=lambda x: x)
  
      # Local URLs shouldn't rely on the output's root, so set empty root
      get_path_for = functools.partial(_get_path_for, Path())
  
      request_urls = fmt(methods, get_path_for)
      type_urls = fmt(types, _get_path_for_type)
      constructor_urls = fmt(cs, get_path_for)
  
      paths['search.js'].parent.mkdir(parents=True, exist_ok=True)
      _copy_replace(input_res / 'js/search.js', paths['search.js'], {
          '{request_names}': request_names,
          '{type_names}': type_names,
          '{constructor_names}': constructor_names,
          '{request_urls}': request_urls,
          '{type_urls}': type_urls,
          '{constructor_urls}': constructor_urls
      })