if not modules then modules = { } end modules ['scrp-cjk'] = {
    version   = 1.001,
    comment   = "companion to scrp-ini.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local has_attribute      = node.has_attribute
local make_glue_node     = nodes.glue
local make_penalty_node  = nodes.penalty
local insert_node_after  = node.insert_after
local insert_node_before = node.insert_before
local remove_node        = nodes.remove

local glyph   = node.id('glyph')
local glue    = node.id('glue')
local penalty = node.id('penalty')

local preproc = attributes.private('preproc')
local prestat = attributes.private('prestat')

scripts.cjk = scripts.cjk or { }

local kind_to_number = scripts.kind_to_number
local number_to_kind = scripts.number_to_kind
local hash           = scripts.hash
local cjk            = scripts.cjk

local fontdata       = fonts.identifiers
local quaddata       = fonts.quads

-- raggedleft is controlled by leftskip and we might end up with a situation where
-- the intercharacter spacing interferes with this; the solution is to patch the
-- nodelist but better is to use veryraggedleft

local inter_char_stretch        = 0
local inter_char_half_shrink    = 0
local inter_char_hangul_penalty = 0

local function set_parameters(font,data)
    -- beware: parameters can be nil in e.g. punk variants
    local quad = quaddata[font]
    inter_char_half_shrink    = data.inter_char_half_shrink_factor * quad
    inter_char_stretch        = data.inter_char_stretch_factor * quad
    inter_char_hangul_penalty = data.inter_char_hangul_penalty
end

-- a test version did compensate for crappy halfwidth but we can best do that
-- at font definition time and/or just assume a correct font

local function nobreak(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
end
local function stretch_break(head,current)
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
end
local function shrink_break(head,current)
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
end
local function nobreak_stretch(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
end
local function korean_break(head,current)
    insert_node_before(head,current,make_penalty_node(inter_char_hangul_penalty))
end

local function nobreak_shrink(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
end
local function nobreak_autoshrink(head,current)
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
end

local function nobreak_stretch_nobreak_shrink(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
end
local function nobreak_stretch_nobreak_autoshrink(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
end

local function nobreak_shrink_nobreak_stretch(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
end
local function nobreak_autoshrink_nobreak_stretch(head,current)
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
end

local function nobreak_shrink_break_stretch(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
end
local function nobreak_autoshrink_break_stretch(head,current)
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
end

local function nobreak_shrink_break_stretch_nobreak_shrink(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
end
local function nobreak_autoshrink_break_stretch_nobreak_autoshrink(head,current)
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
end
local function nobreak_autoshrink_break_stretch_nobreak_shrink(head,current)
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
end
local function nobreak_shrink_break_stretch_nobreak_autoshrink(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    if true then
        insert_node_before(head,current,make_penalty_node(10000))
        insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    end
end

local function nobreak_stretch_break_shrink(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
end
local function nobreak_stretch_break_autoshrink(head,current)
    insert_node_before(head,current,make_penalty_node(10000))
    insert_node_before(head,current,make_glue_node(0,inter_char_stretch,0))
    if true then
        insert_node_before(head,current,make_glue_node(0,0,inter_char_half_shrink))
    end
end

-- hangul (korean)

local injectors = { -- [previous] [current]
    jamo_final = {
        jamo_initial     = korean_break,
        korean           = korean_break,
        chinese          = korean_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak,
        full_width_open  = stretch_break,
        full_width_close = nobreak,
    --  hyphen           = nil,
        non_starter      = korean_break,
        other            = korean_break,
    },
    korean = {
        jamo_initial     = korean_break,
        korean           = korean_break,
        chinese          = korean_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak,
        full_width_open  = stretch_break,
        full_width_close = nobreak,
    --  hyphen           = nil,
        non_starter      = korean_break,
        other            = korean_break,
    },
    chinese = {
        jamo_initial     = korean_break,
        korean           = korean_break,
        chinese          = korean_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak,
        full_width_open  = stretch_break,
        full_width_close = nobreak,
    --  hyphen           = nil,
        non_starter      = korean_break,
        other            = korean_break,
    },
    hyphen = {
        jamo_initial     = stretch_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak,
        full_width_open  = stretch_break,
        full_width_close = nobreak,
    --  hyphen           = nil,
        non_starter      = stretch_break,
        other            = stretch_break,
    },
    start = {
    --  jamo_initial     = nil,
    --  korean           = nil,
    --  chinese          = nil,
    --  half_width_open  = nil,
    --  half_width_close = nil,
    --  full_width_open  = nil,
    --  full_width_close = nil,
    --  hyphen           = nil,
    --  non_starter      = nil,
    --  other            = nil,
    },
    other = {
        jamo_initial     = stretch_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak,
        full_width_open  = stretch_break,
        full_width_close = nobreak,
    --  hyphen           = nil,
        non_starter      = stretch_break,
        other            = stretch_break,
    },
    non_starter = {
        jamo_initial     = stretch_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak,
        full_width_open  = stretch_break,
        full_width_close = nobreak,
    --  hyphen           = nil,
        non_starter      = nobreak,
        other            = nobreak,
    },
    full_width_open = {
        jamo_initial     = nobreak,
        korean           = nobreak,
        chinese          = nobreak,
        half_width_open  = nobreak,
        half_width_close = nobreak,
        full_width_open  = nobreak,
        full_width_close = nobreak,
        hyphen           = nobreak,
        non_starter      = nobreak,
        other            = nobreak,
    },
    half_width_open = {
        jamo_initial     = nobreak,
        korean           = nobreak,
        chinese          = nobreak,
        half_width_open  = nobreak,
        half_width_close = nobreak,
        full_width_open  = nobreak,
        full_width_close = nobreak,
        hyphen           = nobreak,
        non_starter      = nobreak,
        other            = nobreak,
    },
    full_width_close = {
        jamo_initial     = stretch_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak_stretch,
        full_width_open  = stretch_break,
        full_width_close = nobreak_stretch,
        hyphen           = nobreak_stretch,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
    half_width_close = {
        jamo_initial     = stretch_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = stretch_break,
        half_width_close = nobreak_stretch,
        full_width_open  = stretch_break,
        full_width_close = nobreak_stretch,
        hyphen           = nobreak_stretch,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
}

local dataset = {
    inter_char_stretch_factor     = 0.50, -- of quad
    inter_char_half_shrink_factor = 0.50, -- of quad
    inter_char_hangul_penalty     =   50,
}

local function process(head,first,last)
    if first ~= last then
        local lastfont, previous, originals, last = nil, "start", nil, nil
        while true do
            local upcoming, id = first.next, first.id
            if id == glyph then
                local a = has_attribute(first,prestat)
                local current = number_to_kind[a]
                local action = injectors[previous]
                if action then
                    action = action[current]
                    if action then
                        local font = first.font
                        if font ~= lastfont then
                            lastfont, done = font, true
                            set_parameters(font,dataset)
                        end
                        action(head,first)
                    end
                end
                previous = current
            else -- glue
                local p, n = first.prev, upcoming
                if p and n then
                    local pid, nid = p.id, n.id
                    if pid == glyph and nid == glyph then
                        local pa, na = has_attribute(p,prestat), has_attribute(n,prestat)
                        local pcjk, ncjk = pa and number_to_kind[pa], na and number_to_kind[na]
                        if not pcjk                 or not ncjk
                            or pcjk == "korean"     or ncjk == "korean"
                            or pcjk == "other"      or ncjk == "other"
                            or pcjk == "jamo_final" or ncjk == "jamo_initial" then
                            previous = "start"
                        else -- if head ~= first then
                            remove_node(head,first,true)
                            previous = pcjk
                    --    else
                    --        previous = pcjk
                        end
                    else
                        previous = "start"
                    end
                else
                    previous = "start"
                end
            end
            if upcoming == stop then
                break
            else
                first = upcoming
            end
        end
    end
end

scripts.install {
    name = "hangul",
    process = process,
}

-- hanzi (chinese)

local injectors = { -- [previous] [current]
    jamo_final = {
        jamo_initial     = korean_break,
        korean           = korean_break,
        chinese          = stretch_break,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_break_shrink,
        full_width_close = nobreak_stretch,
    --  hyphen           = nil,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
    korean = {
        jamo_initial     = korean_break,
        korean           = korean_break,
        chinese          = stretch_break,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_break_shrink,
        full_width_close = nobreak_stretch,
    --  hyphen           = nil,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
    chinese = {
        jamo_initial     = korean_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_break_shrink,
        full_width_close = nobreak_stretch,
        hyphen           = nobreak_stretch,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
    hyphen = {
        jamo_initial     = korean_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_break_shrink,
        full_width_close = nobreak_stretch,
    --  hyphen           = nil,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
    start = {
    --  jamo_initial     = nil,
    --  korean           = nil,
    --  chinese          = nil,
        half_width_open  = nobreak_autoshrink,
        half_width_close = nil,
        full_width_open  = nobreak_shrink,
        full_width_close = nobreak,
    --  hyphen           = nil,
        non_starter      = nobreak,
    --  other            = nil,
    },
    other = {
        jamo_initial     = stretch_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_break_shrink,
        full_width_close = nobreak_stretch,
    --  hyphen           = nil,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
    non_starter = {
        jamo_initial     = stretch_break,
        korean           = stretch_break,
        chinese          = stretch_break,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_break_shrink,
        full_width_close = nobreak_stretch,
    --  hyphen           = nil,
        non_starter      = nobreak_stretch,
        other            = stretch_break,
    },
    full_width_open = {
        jamo_initial     = nobreak_stretch,
        korean           = nobreak_stretch,
        chinese          = nobreak_stretch,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_break_shrink,
        full_width_close = nobreak_stretch,
        hyphen           = nobreak_stretch,
        non_starter      = nobreak_stretch,
        other            = nobreak_stretch,
    },
    half_width_open = {
        jamo_initial     = nobreak_stretch,
        korean           = nobreak_stretch,
        chinese          = nobreak_stretch,
        half_width_open  = nobreak_stretch_break_autoshrink,
        half_width_close = nobreak_stretch,
        full_width_open  = nobreak_stretch_nobreak_shrink,
        full_width_close = nobreak_stretch,
        hyphen           = nobreak_stretch,
        non_starter      = nobreak_stretch,
        other            = nobreak_stretch,
    },
    full_width_close = {
        jami_initial     = nobreak_shrink_break_stretch,
        korean           = nobreak_shrink_break_stretch,
        chinese          = stretch_break, -- nobreak_shrink_break_stretch,
        half_width_open  = nobreak_shrink_break_stretch_nobreak_autoshrink,
        half_width_close = nobreak_shrink_nobreak_stretch,
        full_width_open  = nobreak_shrink_break_stretch_nobreak_shrink,
        full_width_close = nobreak_shrink_nobreak_stretch,
        hyphen           = nobreak_shrink_break_stretch,
        non_starter      = nobreak_shrink_break_stretch,
        other            = nobreak_shrink_break_stretch,
    },
    half_width_close = {
        jami_initial     = nobreak_shrink_break_stretch,
        korean           = nobreak_autoshrink_break_stretch,
        chinese          = stretch_break, -- nobreak_autoshrink_break_stretch,
        half_width_open  = nobreak_autoshrink_break_stretch_nobreak_autoshrink,
        half_width_close = nobreak_autoshrink_nobreak_stretch,
        full_width_open  = nobreak_autoshrink_break_stretch_nobreak_shrink,
        full_width_close = nobreak_autoshrink_nobreak_stretch,
        hyphen           = nobreak_autoshrink_break_stretch,
        non_starter      = nobreak_autoshrink_break_stretch,
        other            = nobreak_autoshrink_break_stretch,
    },
}

local dataset = {
    inter_char_stretch_factor     = 0.50, -- of quad
    inter_char_half_shrink_factor = 0.50, -- of quad
    inter_char_hangul_penalty     =   50,
}

local function process(head,first,last)
    if first ~= last then
        local lastfont, previous, originals, last = nil, "start", nil, nil
        while true do
            local upcoming, id = first.next, first.id
            if id == glyph then
                local a = has_attribute(first,prestat)
                local current = number_to_kind[a]
                local action = injectors[previous]
                if action then
                    action = action[current]
                    if action then
                        local font = first.font
                        if font ~= lastfont then
                            lastfont, done = font, true
                            set_parameters(font,dataset)
                        end
                        action(head,first)
                    end
                end
                previous = current
            else -- glue
                local p, n = first.prev, upcoming
                if p and n then
                    local pid, nid = p.id, n.id
                    if pid == glyph and nid == glyph then
                        local pa, na = has_attribute(p,prestat), has_attribute(n,prestat)
                        local pcjk, ncjk = pa and number_to_kind[pa], na and number_to_kind[na]
                        if not pcjk                 or not ncjk
                            or pcjk == "korean"     or ncjk == "korean"
                            or pcjk == "other"      or ncjk == "other"
                            or pcjk == "jamo_final" or ncjk == "jamo_initial" then
                            previous = "start"
                        else -- if head ~= first then
                            remove_node(head,first,true)
                            previous = pcjk
                    --    else
                    --        previous = pcjk
                        end
                    else
                        previous = "start"
                    end
                else
                    previous = "start"
                end
            end
            if upcoming == stop then
                break
            else
                first = upcoming
            end
        end
    end
end

scripts.install {
    name = "hanzi",
    process = process,
}
