def add(i):
    """
    Note, that we can't create repos in parallel (recording to repo cache may fail).
    However, for now, we do not expect such cases (i.e. repos are created rarely)

    Input:  {
              (repo_uoa)                 - repo UOA (where to create entry)
              data_uoa                   - data UOA
              (data_uid)                 - data UID (if uoa is an alias)
              (data_name)                - user friendly data name

              (cids[0])                  - as uoa or full CID

              (path)                     - if !='' - create in this path or import from this path
              (here)                     - if =='yes', create in current path
              (use_default_path)         - if 'yes' create repository in the default path (CK_REPOS)
                                           instead of the current path (default is 'yes')

              (use_current_path)         - if 'yes' create repository in the current path
                                           (default is 'no')

              (default)                  - if 'yes', no path is used, 
                                           but the repository is taken either 
                                           from the CK directory or from CK_LOCAL_REPO

              (import)                   - if 'yes', register repo in the current directory in CK
                                           (when received from someone else)

              (remote)                   - if 'yes', remote repository
              (remote_repo_uoa)          - if !='' and type=='remote' repository UOA on the remote CK server

              (shared)                   - if not remote and =='git', repo is shared/synced through GIT
              (share)                    - (for user-friendly CMD) if 'yes', set shared=git

              (allow_writing)            - if 'yes', allow writing 
                                           (useful when kernel is set to allow writing only to such repositories)

              (url)                      - if type=='remote' or 'git', URL of remote repository or git repository
              (hostname)                 - if !='', automatically form url above (add http:// + /ck?)
              (port)                     - if !='', automatically add to url above
              (hostext)                  - if !='', add to the end of above URL instead of '/ck?' -
                                           useful when CK server is accessed via Apache2, IIS, Nginx or other web servers

              (githubuser)               - if shared repo, use this GitHub user space instead of default "ctuning"
              (sync)                     - if 'yes' and type=='git', sync repo after each write operation

              (gitzip)                   - if 'yes', download as zip from GitHub
              (zip)                      - path to zipfile (local or remote http/ftp)
              (overwrite)                - if 'yes', overwrite files when unarchiving

              (repo_deps)                - dict with dependencies on other shared repositories with following keys:
                                             "repo_uoa"
                                             ("repo_uid") - specific UID (version) of a repo
                                             ("repo_url") - URL of the shared repository (if not from github.com/ctuning)

              (quiet)                    - if 'yes', do not ask questions unless really needed

              (skip_reusing_remote_info) - if 'yes', do not reuse remote .cmr.json description of a repository

              (current_repos)            - if resolving dependencies on other repos, list of repos being updated (to avoid infinite recursion)

              (describe)                 - describe repository for Artifact Evaluation (see http://cTuning.org/ae)

              (stable)        - take stable version (highly experimental)
              (version)       - checkout version (default - stable)
              (branch)        - git branch
              (checkout)      - git checkout
            }

    Output: {
              return       - return code =  0, if successful
                                           16, repository with a given path is already registered in CK
                                         >  0, if error
              (error)      - error text if return > 0
            }

    """
    # Check if global writing is allowed
    r=ck.check_writing({})
    if r['return']>0: return r

    o=i.get('out','')

    a=i.get('repo_uoa','')
    d=i.get('data_uoa','')
    di=i.get('data_uid','')
    dn=i.get('data_name','')

    stable=i.get('stable','')
    version=i.get('version','')
    if stable=='yes': version='stable'
    branch=i.get('branch','')
    checkout=i.get('checkout','')

    cr=i.get('current_repos',[])

    rdeps=i.get('repo_deps',[])

    quiet=i.get('quiet','')

    overwrite=i.get('overwrite','')

    remote=i.get('remote','')
    rruoa=i.get('remote_repo_uoa','')
    shared=i.get('shared','')
    if shared=='yes': shared='git'

    share=i.get('share','')
    if share=='yes' and shared=='': shared='git'

    ptr=''
    zp=i.get('zip','')
    gz=i.get('gitzip','')

    if zp!='':
       if zp.startswith('~'):
          from os.path import expanduser
          home = expanduser("~")

          zp=os.path.abspath(home+os.sep+zp[1:])

       quiet='yes'

       if d=='':
          # Try to get data UOA
          if not os.path.isfile(zp):
             return {'return':1, 'error':'zip file not found'}

          # Try to get .ckr.json
          import zipfile

          try:
             with zipfile.ZipFile(zp) as z:
                zip_has_git=False
                if '.git/HEAD' in z.namelist():
                   zip_has_git=True

                if ck.cfg['repo_file'] in z.namelist():
                   x=z.open(ck.cfg['repo_file'])
                   y=x.read()

                   r=ck.convert_json_str_to_dict({'str':y, 'skip_quote_replacement':'yes'})
                   if r['return']>0: return r
                   yd=r['dict']

                   d=yd.get('data_uoa','')
                   di=yd.get('data_uid','')
                   dn=yd.get('data_name','')

                   zip_shared=yd.get('dict',{}).get('shared','')
                   if zip_shared!='':
                      if zip_shared!='git' or zip_has_git:
                         shared=yd['dict']['shared']
                         share='yes'

                   x.close()
             z.close()
          except Exception as e:
             return {'return':1, 'error':'problem reading zip file ('+format(e)+')'}

          if d=='':
             x1=os.path.basename(zp)
             d=os.path.splitext(x1)[0]
             if d.startswith('ckr-'):
                d=d[4:]

             if o=='con':
                ck.out('Auto-detected repo name from zip filename: '+d)

    if gz=='yes':
       zp=ck.cfg['default_shared_repo_url']+'/'+d+'/archive/master.zip'
       ptr=d+'-master/'
       quiet='yes'

    rx=form_url(i)
    if rx['return']>0: return rx
    url=rx['url']

    sync=i.get('sync','')
    df=i.get('default','')

    eaw=i.get('allow_writing','')

    udp=i.get('use_default_path','yes')
    ucp=i.get('use_current_path','')
    if ucp=='yes': udp=''

    # Get repo path (unless 'here' later)
    px=i.get('path','')

    # Check if import
    imp=i.get('import','')
    if imp=='yes': 
       if px=='': i['here']='yes'

    # Get 'here' path
    if i.get('here','')=='yes': px=os.getcwd()
    p=px

    if imp=='yes': 
       py=os.path.join(p,ck.cfg['repo_file'])
       if os.path.isfile(py):
          r=ck.load_json_file({'json_file':py})
          if r['return']>0: return r
          dc=r['dict']

          d=dc.get('data_uoa','')
          di=dc.get('data_uid','')
          dn=dc.get('data_name','')

    if p=='' and udp=='yes': p=os.path.join(ck.work['dir_repos'], d)

    # Normalize path
    p=os.path.normpath(p)

    # If console mode, first, check if shared (GIT, etc)
    if o=='con':
       # Asking for alias
       if df!='yes' and (d=='' or ck.is_uid(d)):
          r=ck.inp({'text':'Enter an alias for this repository (or Enter to skip it): '})
          d=r['string']
          if d=='': d=di
          if d=='':
             r=ck.gen_uid({})
             if r['return']>0: return r
             di=r['data_uid']
             d=di

       # Asking for a user-friendly name
       if df!='yes' and dn=='':
          if quiet!='yes':
             r=ck.inp({'text':'Enter a user-friendly name of this repository (or Enter to reuse alias): '})
             dn=r['string']
          if dn=='': dn=d

       # Asking if remote
       if df!='yes' and remote=='':
          if quiet!='yes':
             r=ck.inp({'text':'Is this repository a remote CK web service (y/N)? '})
             remote=r['string'].lower()
          if remote=='yes' or remote=='y': remote='yes'
          else: remote=''

       # Asking for a user-friendly name
       if px=='' and df!='yes' and remote!='yes' and udp=='':
          if quiet!='yes':
             r=ck.inp({'text':'Would you like to create repo in the directory from CK_REPOS variable (Y/n): '})
             x=r['string'].lower()
             if x=='' or x=='yes' or x=='y':
                p=os.path.join(ck.work['dir_repos'], d)

       # Asking for remote url
       if df!='yes' and remote=='yes' and url=='':
          if quiet!='yes':
             r=ck.inp({'text':'Enter URL of remote CK repo (example: http://localhost:3344/ck?): '})
             url=r['string'].lower()
          if url=='':
             return {'return':1, 'error':'URL is empty'}

       # Asking for remote repo UOA
       if df!='yes' and remote=='yes' and rruoa=='':
          if quiet!='yes':
             r=ck.inp({'text':'Enter remote repo UOA or Enter to skip: '})
             rruoa=r['string'].lower()

       # Asking for shared
       if remote=='' and shared=='' and share=='':
          if quiet!='yes':
             r=ck.inp({'text':'Is this repository shared via GIT (y/N)? '})
             x=r['string'].lower()
             if x=='yes' or x=='y':
                share='yes'

       if share=='yes' and shared=='': shared='git'

       # Check additional parameters if git
       ghu=i.get('githubuser','')
       if shared=='git' and url=='':

          if ghu!='': 
             durl=ck.cfg.get('github_repo_url','')
             if not durl.endswith('/'): durl+='/'
             durl+=ghu
          else: durl=ck.cfg.get('default_shared_repo_url','')

          if not durl.endswith('/'): durl+='/'
          durl+=d
#          if durl.startswith('http://') or durl.startswith('https://'):
#             durl+='.git'

          if quiet!='yes':
             s='Enter URL of GIT repo '
             if d=='': s+='(for example, https://github.com/ctuning/ck-analytics.git)'
             else:     s+='(or Enter for '+durl+')'
             r=ck.inp({'text': s+': '})
             url=r['string'].lower()
          if url=='': url=durl

       # Check additional parameters if git
       if shared=='git' and sync=='':
          if quiet!='yes':
             r=ck.inp({'text': 'Would you like to sync repo each time after writing to it (y/N)?: '})
             x=r['string'].lower()
             if x=='yes' or x=='y':
                sync='yes'

       # Asking for a user-friendly name
       if df!='yes' and remote!='yes' and eaw=='':
          if quiet!='yes':
             r=ck.inp({'text':'Would you like to explicitly allow writing to this repository in case kernel disables all writing (y/N): '})
             x=r['string'].lower()
             if x=='yes' or x=='y':
                eaw='yes'

       # Check if add more deps
       if quiet!='yes':
          r=add_more_deps({})
          if r['return']>0: return r

          rdeps1=r['repo_deps']
          for q in rdeps1:
              rdeps.append(q)

    # Check if already registered (if not remote)
    if remote!='yes':
       r=ck.find_repo_by_path({'path':p})
       if r['return']>0 and r['return']!=16: 
          return r

    # Check if repository is already registered with this path
    r=ck.find_repo_by_path({'path':p})
    if r['return']==0:
       return {'return':16, 'error':'repository with a given path is already registered in CK'}
    elif r['return']!=16: 
       return r

    # Prepare local description file
    py=os.path.join(p,ck.cfg['repo_file'])

    # Create dummy if doesn't exist
    if remote!='yes' and not os.path.isdir(p):
       os.makedirs(p)

    # If zip, get (download) and unzip file ...
    if zp!='':
       rz=get_and_unzip_archive({'zip':zp, 'path':p, 'path_to_remove':ptr, 'overwrite':overwrite, 'out':o})
       if rz['return']>0: return rz

       py = os.path.join(p, ck.cfg['repo_file'])
       if os.path.isfile(py):
           r = ck.load_json_file({'json_file': py})
           if r['return'] > 0: return r
           dc = r['dict']

           d = dc.get('data_uoa', '')
           di = dc.get('data_uid', '')
           dn = dc.get('data_name', '')

    # If git, clone repo
    repo_had_local=True
    dd={}
    if remote!='yes' and shared=='git' and zp=='':
       r=pull({'path':p, 'type':shared, 'url':url, 'clone':'yes', 'git':i.get('git',''), 'out':o})
       if r['return']>0: return r

       # Check if there is a local repo description
       if os.path.isfile(py):
          r=ck.load_json_file({'json_file':py})
          if r['return']>0: return r
          dc=r['dict']
          ddc=dc.get('dict',{})
          dd.update(ddc)

          xd=dc.get('data_uoa','')
          xdi=dc.get('data_uid','')
          xdn=dc.get('data_name','')

          if o=='con':
             ck.out('Cloned repository has the following info:')
             ck.out(' UID                = '+xdi)
             ck.out(' UOA                = '+xd)
             ck.out(' User friendly name = '+xdn)
             ck.out('')
          if i.get('skip_reusing_remote_info','')!='yes':
             d=xd
             di=xdi
             dn=xdn
       else:
          repo_had_local=False

    # Prepare meta description
    if df=='yes': 
       dd['default']='yes'
    if remote=='yes': 
       dd['remote']='yes'
       if rruoa!='': 
          dd['remote_repo_uoa']=rruoa
    if shared!='':
       dd['shared']=shared
       if sync!='': 
          dd['sync']=sync
    if url!='': 
       dd['url']=url
    if remote!='yes': 
       dd['path']=p
    if eaw=='yes':
       dd['allow_writing']='yes'
    if len(rdeps)>0:
       dd['repo_deps']=rdeps

    # Check if need to describe for Artifact Evaluation
    if i.get('describe','')=='yes':
       r=describe({'dict':dd})
       if r['return']>0: return r

    # If not default, go to common core function to create entry
    if df!='yes':
       ii={'module_uoa':work['self_module_uoa'],
           'action':'add',
           'data_uoa':d,
           'dict':dd,
           'common_func':'yes'}
       if a!='': ii['repo_uoa']=a
       if di!='': ii['data_uid']=di
       if dn!='': ii['data_name']=dn
       rx=ck.access(ii)
       if rx['return']>0: return rx
    else:
       # Load default repo and prepare return
       ii={'module_uoa':work['self_module_uoa'],
           'action':'load',
           'data_uoa':'default',
           'common_func':'yes'}
       rx=ck.access(ii)
    px=rx['path']
    dx=rx['data_uid']
    alias=rx['data_alias']

    # Update repo cache if not default local
    dz={'data_uoa':d, 'data_uid':dx, 'data_alias':alias, 'path_to_repo_desc':px, 'data_name':dn, 'dict':dd}

    if df!='yes':
       r=ck.reload_repo_cache({}) # Ignore errors
       ck.cache_repo_uoa[d]=dx
       ck.cache_repo_info[dx]=dz
       r=ck.save_repo_cache({})
       if r['return']>0: return r

    # Record local info of the repo (just in case)
    if remote!='yes':
       if 'path_to_repo_desc' in dz: del (dz['path_to_repo_desc'])        # Avoid recording some local info
       if dz.get('dict',{}).get('path','')!='': del (dz['dict']['path'])  # Avoid recording some local info

       if not os.path.isfile(py):
          ry=ck.save_json_to_file({'json_file':py, 'dict':dz})
          if ry['return']>0: return ry

       # If sync (or pulled repo did not have local description), add it ...
       if sync=='yes' or (shared=='git' and not repo_had_local):
          ppp=os.getcwd()

          os.chdir(p)
          ss=ck.cfg['repo_types'][shared]['add'].replace('$#path#$', px).replace('$#files#$', ck.cfg['repo_file'])
          os.system(ss)

          os.chdir(ppp)

    # If console mode, print various info
    if o=='con':
       ck.out('')
       ck.out('CK repository successfully registered!')
       ck.out('')

       if df!='yes':
          ck.out('CK repo description path = '+px)
          ck.out('CK repo UID              = '+dx)

    # Recache repos otherwise may be problems with deps
    if o=='con':
       ck.out('')
       ck.out('Recaching repos to speed up access ...')
       ck.out('')
    r=recache({'out':o})
    if r['return']>0: return r

    # Check deps
    if o=='con':
       ck.out('  ========================================')
       ck.out('  Checking dependencies on other repos ...')
       ck.out('')

    how='pull'
    if gz=='yes': how='add'
    r=deps({'path':p,
            'current_path':cr,
            'how':how,
            'version':version,
            'branch':branch,
            'checkout':checkout,
            'out':o})
    if r['return']>0: return r

    # Print if default
    if df=='yes' and o=='con':
       ck.out('')
       ck.out('Please, do not forget to add path to this repository to CK_LOCAL_REPO environment variable:')
       ck.out('')
       ck.out('  Linux: export CK_LOCAL_REPO='+p)
       ck.out('  Windows: set CK_LOCAL_REPO='+p)

    return rx
Note, that we can't create repos in parallel (recording to repo cache may fail).
    However, for now, we do not expect such cases (i.e. repos are created rarely)

    Input:  {
              (repo_uoa)                 - repo UOA (where to create entry)
              data_uoa                   - data UOA
              (data_uid)                 - data UID (if uoa is an alias)
              (data_name)                - user friendly data name

              (cids[0])                  - as uoa or full CID

              (path)                     - if !='' - create in this path or import from this path
              (here)                     - if =='yes', create in current path
              (use_default_path)         - if 'yes' create repository in the default path (CK_REPOS)
                                           instead of the current path (default is 'yes')

              (use_current_path)         - if 'yes' create repository in the current path
                                           (default is 'no')

              (default)                  - if 'yes', no path is used, 
                                           but the repository is taken either 
                                           from the CK directory or from CK_LOCAL_REPO

              (import)                   - if 'yes', register repo in the current directory in CK
                                           (when received from someone else)

              (remote)                   - if 'yes', remote repository
              (remote_repo_uoa)          - if !='' and type=='remote' repository UOA on the remote CK server

              (shared)                   - if not remote and =='git', repo is shared/synced through GIT
              (share)                    - (for user-friendly CMD) if 'yes', set shared=git

              (allow_writing)            - if 'yes', allow writing 
                                           (useful when kernel is set to allow writing only to such repositories)

              (url)                      - if type=='remote' or 'git', URL of remote repository or git repository
              (hostname)                 - if !='', automatically form url above (add http:// + /ck?)
              (port)                     - if !='', automatically add to url above
              (hostext)                  - if !='', add to the end of above URL instead of '/ck?' -
                                           useful when CK server is accessed via Apache2, IIS, Nginx or other web servers

              (githubuser)               - if shared repo, use this GitHub user space instead of default "ctuning"
              (sync)                     - if 'yes' and type=='git', sync repo after each write operation

              (gitzip)                   - if 'yes', download as zip from GitHub
              (zip)                      - path to zipfile (local or remote http/ftp)
              (overwrite)                - if 'yes', overwrite files when unarchiving

              (repo_deps)                - dict with dependencies on other shared repositories with following keys:
                                             "repo_uoa"
                                             ("repo_uid") - specific UID (version) of a repo
                                             ("repo_url") - URL of the shared repository (if not from github.com/ctuning)

              (quiet)                    - if 'yes', do not ask questions unless really needed

              (skip_reusing_remote_info) - if 'yes', do not reuse remote .cmr.json description of a repository

              (current_repos)            - if resolving dependencies on other repos, list of repos being updated (to avoid infinite recursion)

              (describe)                 - describe repository for Artifact Evaluation (see http://cTuning.org/ae)

              (stable)        - take stable version (highly experimental)
              (version)       - checkout version (default - stable)
              (branch)        - git branch
              (checkout)      - git checkout
            }

    Output: {
              return       - return code =  0, if successful
                                           16, repository with a given path is already registered in CK
                                         >  0, if error
              (error)      - error text if return > 0
            }